From 1ecf3c82b85c464fca8f8b3cf576d57f7da68ac2 Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Mon, 10 Nov 2025 18:43:44 -0800 Subject: [PATCH 001/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Shaders/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Shaders/README.md diff --git a/Shaders/README.md b/Shaders/README.md new file mode 100644 index 00000000..194482fc --- /dev/null +++ b/Shaders/README.md @@ -0,0 +1 @@ +This directory contains some additional Pixel Shaders that are not included in [obs-shader-filter](https://github.com/exeldro/obs-shaderfilter/) \ No newline at end of file From ec15a6d6a5846480d14cefb9527aae847f0869b8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:47 +0000 Subject: [PATCH 002/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBS3dPanelShader.ps1 | 450 ++++++++++++++++++++++ 1 file changed, 450 insertions(+) create mode 100644 Commands/Shaders/Get-OBS3dPanelShader.ps1 diff --git a/Commands/Shaders/Get-OBS3dPanelShader.ps1 b/Commands/Shaders/Get-OBS3dPanelShader.ps1 new file mode 100644 index 00000000..bef20bdf --- /dev/null +++ b/Commands/Shaders/Get-OBS3dPanelShader.ps1 @@ -0,0 +1,450 @@ +function Get-OBS3dPanelShader { + +[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] +param( +# Set the credits of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('credits')] +[String] +$Credits, +# Set the scale of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the tilt_x_deg of OBS3dPanelShader +[Alias('tilt_x_deg')] +[ComponentModel.DefaultBindingProperty('tilt_x_deg')] +[Single] +$TiltXDeg, +# Set the tilt_y_deg of OBS3dPanelShader +[Alias('tilt_y_deg')] +[ComponentModel.DefaultBindingProperty('tilt_y_deg')] +[Single] +$TiltYDeg, +# Set the tilt_z_deg of OBS3dPanelShader +[Alias('tilt_z_deg')] +[ComponentModel.DefaultBindingProperty('tilt_z_deg')] +[Single] +$TiltZDeg, +# Set the pos_x of OBS3dPanelShader +[Alias('pos_x')] +[ComponentModel.DefaultBindingProperty('pos_x')] +[Single] +$PosX, +# Set the pos_y of OBS3dPanelShader +[Alias('pos_y')] +[ComponentModel.DefaultBindingProperty('pos_y')] +[Single] +$PosY, +# Set the thickness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('thickness')] +[Single] +$Thickness, +# Set the radius_fb of OBS3dPanelShader +[Alias('radius_fb')] +[ComponentModel.DefaultBindingProperty('radius_fb')] +[Single] +$RadiusFb, +# Set the brightness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the light_position of OBS3dPanelShader +[Alias('light_position')] +[ComponentModel.DefaultBindingProperty('light_position')] +[Int32] +$LightPosition, +# Set the wiggle of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('wiggle')] +[Single] +$Wiggle, +# Set the wiggle_rot of OBS3dPanelShader +[Alias('wiggle_rot')] +[ComponentModel.DefaultBindingProperty('wiggle_rot')] +[Management.Automation.SwitchParameter] +$WiggleRot, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = '3d-panel' +$ShaderNoun = 'OBS3dPanelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://x.com/HoraiChan/status/1986268258883010766 + +uniform string credits< + string widget_type = "info"; +> = "Based on effect by Horaiken"; + +uniform float scale< + string label = "大きさ / Scale"; + string widget_type = "slider"; + float minimum = 0.25; + float maximum = 3.00; + float step = 0.001; +> = 1.0; +uniform float tilt_x_deg< + string label = "縦方向の傾き(X) / Tilt (X)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 20.0; +uniform float tilt_y_deg< + string label = "横方向の傾き(Y) / Tilt (Y)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 35.0; +uniform float tilt_z_deg< + string label = "回転(Z) / Roll (Z)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 0.0; +uniform float pos_x< + string label = "横位置 / Horizontal Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float pos_y< + string label = "縦位置 / Vertical Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float thickness< + string label = "厚み / Thickness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 0.1; + float step = 0.001; +> = 0.03; +uniform float radius_fb< + string label = "角丸 / Corner Radius"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.2; +uniform float brightness< + string label = "明るさ / Brightness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.00; + float step = 0.01; +> = 1.2; +uniform int light_position < + string label = "照明の位置 / Light Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "左側に光 / Light From Left"; + int option_1_value = 1; + string option_1_label = "右側に光 / Light From Right"; +> = 0; +uniform float wiggle < + string label = "ゆらゆら / Wiggle"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.50; + float step = 0.01; +> = 0.0; +uniform bool wiggle_rot < + string label = "角度もゆらゆら / Wiggle Rotation"; +>; + +float hash1(float n){ return frac(sin(n)*43758.5453123); } + +float noise1D(float x) { + float i = floor(x); + float f = frac(x); + float u = f*f*(3.0 - 2.0*f); + return lerp(hash1(i), hash1(i+1.0), u); // 0..1 +} + +float fbm1D(float x) { + float v = 0.0; + float a = 0.5; + float f = 1.0; + for(int k=0;k<4;k++){ + v += a * noise1D(x * f); + f *= 2.0; + a *= 0.5; + } + return v; +} + +float saturate(float x) { return clamp(x, 0.0, 1.0); } + +float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } +float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } +float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } + +// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) +float sdRoundRect2D(float2 p, float2 bxy, float r) { + float2 q = abs(p) - bxy + r; + return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; +} + +// 正面シルエット角丸 + Z方向に押し出し +float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { + float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); + float a = sdRoundRect2D(p.xy, b.xy, r_fb); + float dz = abs(p.z) - b.z; + return max(a, dz); +} + +// 法線 +float3 calcNormal(float3 p, float3 b, float rfb) { + const float e = 0.001; + float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); + float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); + float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); + float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); + return normalize(float3(dx,dy,dz)); +} + +// 照明 +float3 shade(float3 n, float3 v) { + float3 l; + if (light_position == 0) { // 左から光 + l = normalize(float3(-1.0, -0.1, 1.0)); + } + else { // 右から光 + l = normalize(float3( 1.0, -0.1, 1.0)); + } + float diff = saturate(dot(n,l)); + float rim = pow(1.0 - saturate(dot(n,v)), 2.0); + float li = 0.25 + 0.75*diff + 0.08*rim; + return float3(li, li, li); +} + +float4 mainImage(VertData v_in) : TARGET { + float2 uv = v_in.uv; + + // 画面座標(短辺基準) + float aspect = uv_size.x / uv_size.y; + float2 ndc = uv * 2.0 - 1.0; + ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); + float2 p2 = ndc; + p2.x *= aspect; + + // カメラ設定 + float3 ro = float3(0.0, 0.0, 3.2); + float3 rd = normalize(float3(p2, -4.0)); + + // 回転(Z→Y→X の順に逆回転) + float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); + ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); + rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); + + // 画面フィット(短辺基準)+ 厚み + float2 baseXY; + if (aspect > 1.0) { + baseXY = float2(1.0, 1.0 / aspect); + } else { + const float portraitMargin = 0.6; + baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); + } + float3 b = float3(baseXY, thickness) * max(scale, 0.0001); + + // Wiggle + float diag = length(2.0 * b); + float amp = 0.05 * wiggle * diag; + const float WSPD = 0.1; + + // 各軸に独立ノイズ + float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; + float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; + float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; + float3 woff = float3(wx, wy, wz); + + float rotAmp = radians(12.0) * wiggle; + + float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; + float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; + + float3 ro2 = ro; + float3 rd2 = rd; + + if (wiggle_rot) { + ro2 = rotateX(ro2, wobX); + ro2 = rotateY(ro2, wobY); + rd2 = rotateX(rd2, wobX); + rd2 = rotateY(rd2, wobY); + } + + float t = 0.0; + float d = 0.0; + bool hit = false; + for (int i=0; i<64; i++) { + float3 pos = ro2 + rd2 * t; + d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); + if (d < 0.001) { hit = true; break; } + t += d; + if (t > 8.0) break; + } + + // ヒットしなければ完全透明(元ソースは非表示) + if (!hit) return float4(0.0, 0.0, 0.0, 0.0); + + float3 pos = ro2 + rd2 * t; + float3 pObj = pos - woff; + float3 n = calcNormal(pObj, b, radius_fb); + float3 vdir = normalize(-rd2); + + // テクスチャ貼り付け + float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); + float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; + + // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) + float4 texFront = image.Sample(textureSampler, uvTex); + float4 texEdge = image.Sample(textureSampler, uvTex); + float4 tex = lerp(texEdge, texFront, frontMask); + + // フロント面エッジ・ハイライト(細い線) + float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); + float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF + float edgeWidth = 0.02 * min(b.x, b.y); + float edgeIntensity = 0.6; + float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); + float edgeMask = frontMask * edgeProx; + tex.rgb *= (1.0 + edgeMask * edgeIntensity); + + // 照明 + float3 lightTerm = shade(n, vdir); + tex.rgb *= lightTerm; + + // 明るさスライダ + tex.rgb *= brightness; + + // 出力 + return float4(tex.rgb, 1.0); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 4751ad3048450e7b1de5a17b74bb00a45d6e7542 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:48 +0000 Subject: [PATCH 003/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Shaders/Get-OBSCubeRotatingShader.ps1 | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 Commands/Shaders/Get-OBSCubeRotatingShader.ps1 diff --git a/Commands/Shaders/Get-OBSCubeRotatingShader.ps1 b/Commands/Shaders/Get-OBSCubeRotatingShader.ps1 new file mode 100644 index 00000000..b60a0522 --- /dev/null +++ b/Commands/Shaders/Get-OBSCubeRotatingShader.ps1 @@ -0,0 +1,234 @@ +function Get-OBSCubeRotatingShader { + +[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] +param( +# Set the images of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('images')] +[Int32] +$Images, +# Set the speed of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the shadow of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('shadow')] +[Single] +$Shadow, +# Set the other_image1 of OBSCubeRotatingShader +[Alias('other_image1')] +[ComponentModel.DefaultBindingProperty('other_image1')] +[String] +$OtherImage1, +# Set the other_image2 of OBSCubeRotatingShader +[Alias('other_image2')] +[ComponentModel.DefaultBindingProperty('other_image2')] +[String] +$OtherImage2, +# Set the other_image3 of OBSCubeRotatingShader +[Alias('other_image3')] +[ComponentModel.DefaultBindingProperty('other_image3')] +[String] +$OtherImage3, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'cube_rotating' +$ShaderNoun = 'OBSCubeRotatingShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int images< + string label = "Images"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "1"; + int option_1_value = 2; + string option_1_label = "2"; + int option_2_value = 4; + string option_2_label = "4"; +> = 1; + +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; + +uniform float shadow< + string label = "Shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.5; + float step = 0.001; +> = 1.0; + +uniform texture2d other_image1; +uniform texture2d other_image2; +uniform texture2d other_image3; + +#define PI 3.14159265359 +float4 mainImage(VertData v_in) : TARGET +{ + float t = elapsed_time * speed; + float4 c = float4(0,0,0,0); + for(float side = 0.0; side<4.0; side += 1.0){ + float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; + float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; + if(left < right){ + float2 uv; + uv.x = (v_in.uv.x-left)/(right-left); + float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; + float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; + float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); + uv.y = (v_in.uv.y-0.5)*size+0.5; + float4 sample = float4(0,0,0,0); + if(images <= 1 || side == 0.0){ + sample = image.Sample(textureSampler, uv); + }else if(images == 2){ + if(side == 1.0 || side == 3.0){ + sample = other_image1.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + }else if(images == 4){ + if(side == 1.0){ + sample = other_image1.Sample(textureSampler, uv); + }else if(side == 2.0){ + sample = other_image2.Sample(textureSampler, uv); + }else if(side == 3.0){ + sample = other_image3.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + } + if(sample.a > 0.0){ + c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); + } + } + } + return c; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 9145575a461c7f48a9c0e20b943099b9544ea8b9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:48 +0000 Subject: [PATCH 004/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSCutRectPerCornerShader.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Commands/Shaders/Get-OBSCutRectPerCornerShader.ps1 b/Commands/Shaders/Get-OBSCutRectPerCornerShader.ps1 index 6c8ac1a7..a0abaf03 100644 --- a/Commands/Shaders/Get-OBSCutRectPerCornerShader.ps1 +++ b/Commands/Shaders/Get-OBSCutRectPerCornerShader.ps1 @@ -88,35 +88,35 @@ uniform int corner_tl< int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int corner_tr< string label = "Corner top right"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int corner_br< string label = "Corner bottom right"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int corner_bl< string label = "Corner bottom left"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "Border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_color; uniform float border_alpha_start< string label = "Border aplha start"; From 8a292633f9d3d48ada1b0edcde5aeffa87077eb5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:48 +0000 Subject: [PATCH 005/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSHardBlinkShader.ps1 | 173 ++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 Commands/Shaders/Get-OBSHardBlinkShader.ps1 diff --git a/Commands/Shaders/Get-OBSHardBlinkShader.ps1 b/Commands/Shaders/Get-OBSHardBlinkShader.ps1 new file mode 100644 index 00000000..606b50cd --- /dev/null +++ b/Commands/Shaders/Get-OBSHardBlinkShader.ps1 @@ -0,0 +1,173 @@ +function Get-OBSHardBlinkShader { + +[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] +param( +# Set the timeon of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeon')] +[Single] +$Timeon, +# Set the timeoff of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeoff')] +[Single] +$Timeoff, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'hard_blink' +$ShaderNoun = 'OBSHardBlinkShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// hard_blink shader created by https://github.com/WhazzItToYa +// +// Periodically makes the source image 100% transparent, in configurable intervals. + +uniform float timeon< + string label = "Time On"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; + +uniform float timeoff< + string label = "Time Off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float m = timeon + timeoff; + float t = elapsed_time % m; + if (t < timeon) { + return color; + } else { + return float4(color.r, color.g, color.b, 0.0); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 738fd579d84fda9f88ed96b67eac5d89e9b1047e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:48 +0000 Subject: [PATCH 006/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSMotionBlurShader.ps1 | 155 +++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Commands/Shaders/Get-OBSMotionBlurShader.ps1 diff --git a/Commands/Shaders/Get-OBSMotionBlurShader.ps1 b/Commands/Shaders/Get-OBSMotionBlurShader.ps1 new file mode 100644 index 00000000..19b1aa4a --- /dev/null +++ b/Commands/Shaders/Get-OBSMotionBlurShader.ps1 @@ -0,0 +1,155 @@ +function Get-OBSMotionBlurShader { + +[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] +param( +# Set the previous_output of OBSMotionBlurShader +[Alias('previous_output')] +[ComponentModel.DefaultBindingProperty('previous_output')] +[String] +$PreviousOutput, +# Set the strength of OBSMotionBlurShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'motion_blur' +$ShaderNoun = 'OBSMotionBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d previous_output; +uniform float strength< + string label = "strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +float4 mainImage(VertData v_in) : TARGET +{ + return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 2e00c9021f74fa7746b46402961c64cc2b9322c1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 007/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSPerspectiveShader.ps1 | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 Commands/Shaders/Get-OBSPerspectiveShader.ps1 diff --git a/Commands/Shaders/Get-OBSPerspectiveShader.ps1 b/Commands/Shaders/Get-OBSPerspectiveShader.ps1 new file mode 100644 index 00000000..37be6166 --- /dev/null +++ b/Commands/Shaders/Get-OBSPerspectiveShader.ps1 @@ -0,0 +1,256 @@ +function Get-OBSPerspectiveShader { + +[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] +param( +# Set the angle_x of OBSPerspectiveShader +[Alias('angle_x')] +[ComponentModel.DefaultBindingProperty('angle_x')] +[Single] +$AngleX, +# Set the angle_y of OBSPerspectiveShader +[Alias('angle_y')] +[ComponentModel.DefaultBindingProperty('angle_y')] +[Single] +$AngleY, +# Set the angle_z of OBSPerspectiveShader +[Alias('angle_z')] +[ComponentModel.DefaultBindingProperty('angle_z')] +[Single] +$AngleZ, +# Set the perspective of OBSPerspectiveShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the border_color of OBSPerspectiveShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the show_border of OBSPerspectiveShader +[Alias('show_border')] +[ComponentModel.DefaultBindingProperty('show_border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'perspective' +$ShaderNoun = 'OBSPerspectiveShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Perspective Transform Shader for OBS +// Allows adjustable 3D perspective effects +// Usage: Add as filter in OBS via ShaderFilter plugin + +uniform float angle_x< + string label = "X Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; + +uniform float angle_y< + string label = "Y Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; + +uniform float angle_z< + string label = "Z Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; + +uniform float perspective< + string label = "Perspective Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; + +uniform float4 border_color< + string label = "Border Color"; +> = {0.0, 0.0, 0.0, 1.0}; + +uniform bool show_border< + string label = "Show Border"; +> = true; + +float4x4 rotationMatrix(float3 angles) +{ + float radX = radians(angles.x); + float radY = radians(angles.y); + float radZ = radians(angles.z); + + float sinX = sin(radX); + float cosX = cos(radX); + float sinY = sin(radY); + float cosY = cos(radY); + float sinZ = sin(radZ); + float cosZ = cos(radZ); + + return float4x4( + cosY*cosZ, -cosY*sinZ, sinY, 0, + sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, + -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, + 0, 0, 0, 1 + ); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + + // Center coordinates + float2 center = float2(0.5, 0.5); + uv -= center; + + // Apply perspective + float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); + uv *= perspectiveFactor; + + // Create rotation matrix + float3 angles = float3(angle_x, angle_y, angle_z); + float4x4 rotMat = rotationMatrix(angles); + + // Apply transformation + float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); + + // Restore center position + uv = transformed.xy + center; + + // Sample texture with border handling + if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { + return show_border ? border_color : float4(0, 0, 0, 0); + } + + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 6cefac4220f65fb65a40c1fdc3aad3aa48e6608e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 008/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Get-OBSRepeatGridCenterCropShader.ps1 | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 Commands/Shaders/Get-OBSRepeatGridCenterCropShader.ps1 diff --git a/Commands/Shaders/Get-OBSRepeatGridCenterCropShader.ps1 b/Commands/Shaders/Get-OBSRepeatGridCenterCropShader.ps1 new file mode 100644 index 00000000..8a15a900 --- /dev/null +++ b/Commands/Shaders/Get-OBSRepeatGridCenterCropShader.ps1 @@ -0,0 +1,204 @@ +function Get-OBSRepeatGridCenterCropShader { + +[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] +param( +# Set the ViewProj of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the alpha of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('alpha')] +[Single] +$Alpha, +# Set the columns of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('columns')] +[Single] +$Columns, +# Set the rows of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('rows')] +[Single] +$Rows, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'repeat_grid_center_crop' +$ShaderNoun = 'OBSRepeatGridCenterCropShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// repeat_grid_center_crop.effect + +uniform float4x4 ViewProj; +uniform texture2d image; +sampler_state def_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +uniform float alpha = 1.0; +uniform float columns = 3.0; +uniform float rows = 1.0; + +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertInOut VSDefault(VertInOut vert_in) { + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1), ViewProj); + vert_out.uv = vert_in.uv * float2(columns, rows); + return vert_out; +} + +float4 PSMain(VertInOut vert_in) : TARGET { + // Calculate fractional UV within grid cell + float2 cell_uv = frac(vert_in.uv); + + // Calculate center crop parameters + float horizontalCropStart = 0.5 - 0.5/columns; + + // Map to centered portion of original image + float2 original_uv = float2( + cell_uv.x / columns + horizontalCropStart, + cell_uv.y / rows + ); + + // Sample the image + float4 col = image.Sample(def_sampler, original_uv); + col.a *= alpha; + return col; +} + +technique Draw { + pass { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSMain(vert_in); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 5f95b5bf7bf6e3c2739378b2d27b5d7032da7b2d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 009/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedRectPerCornerShader.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedRectPerCornerShader.ps1 b/Commands/Shaders/Get-OBSRoundedRectPerCornerShader.ps1 index 5777d1b3..512d30d0 100644 --- a/Commands/Shaders/Get-OBSRoundedRectPerCornerShader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedRectPerCornerShader.ps1 @@ -88,35 +88,35 @@ uniform int corner_radius_tl< int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_tr< string label = "Corner radius top right"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_br< string label = "Corner radius bottom right"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_bl< string label = "Corner radius bottom left"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "Border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_color; uniform float border_alpha_start< string label = "border alpha start"; From e36bc0045f330c9f37510a93e7e984aa74cc135f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 010/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedRectPerSideShader.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedRectPerSideShader.ps1 b/Commands/Shaders/Get-OBSRoundedRectPerSideShader.ps1 index cfd42586..9a192fd0 100644 --- a/Commands/Shaders/Get-OBSRoundedRectPerSideShader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedRectPerSideShader.ps1 @@ -87,35 +87,35 @@ uniform int corner_radius_bottom< int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_left< string label = "Corner radius left"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_top< string label = "Corner radius top"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int corner_radius_right< string label = "Corner radius right"; string widget_type = "slider"; int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "Border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_color; uniform float border_alpha_start< string label = "border alpha start"; From 242a6e3e0d1c3a08bc7b4ce42c07aed27d6fdb76 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 011/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedRectShader.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedRectShader.ps1 b/Commands/Shaders/Get-OBSRoundedRectShader.ps1 index 0e3cb8cf..f4eb5afa 100644 --- a/Commands/Shaders/Get-OBSRoundedRectShader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedRectShader.ps1 @@ -58,14 +58,14 @@ uniform int corner_radius< int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_color; From 67e36b3e1c8dd79da686a327d79d4886778aba88 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 012/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedRect2Shader.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedRect2Shader.ps1 b/Commands/Shaders/Get-OBSRoundedRect2Shader.ps1 index 531f5ab9..f72f8dcc 100644 --- a/Commands/Shaders/Get-OBSRoundedRect2Shader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedRect2Shader.ps1 @@ -77,14 +77,14 @@ uniform int corner_radius< int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "Border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_color; uniform float border_alpha_start< string label = "Border alpha start"; From 703e238d607355acf80bca2f17d1e527f936d884 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 013/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedStrokeGradientShader.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedStrokeGradientShader.ps1 b/Commands/Shaders/Get-OBSRoundedStrokeGradientShader.ps1 index 4d946858..2ce530b6 100644 --- a/Commands/Shaders/Get-OBSRoundedStrokeGradientShader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedStrokeGradientShader.ps1 @@ -88,14 +88,14 @@ uniform int corner_radius< int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "Border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int minimum_alpha_percent< string label = "Minimum alpha percent"; string widget_type = "slider"; @@ -109,7 +109,7 @@ uniform int rotation_speed< int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform float4 border_colorL; uniform float4 border_colorR; //uniform float color_spread = 2.0; From 36a01a370a391fd4feddd3dba45f075a6f63c1f5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:49 +0000 Subject: [PATCH 014/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSRoundedStrokeShader.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/Shaders/Get-OBSRoundedStrokeShader.ps1 b/Commands/Shaders/Get-OBSRoundedStrokeShader.ps1 index 6b3fbdd2..17b51bf8 100644 --- a/Commands/Shaders/Get-OBSRoundedStrokeShader.ps1 +++ b/Commands/Shaders/Get-OBSRoundedStrokeShader.ps1 @@ -69,14 +69,14 @@ uniform int corner_radius< int minimum = 0; int maximum = 200; int step = 1; ->; +> = 0; uniform int border_thickness< string label = "border thickness"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; ->; +> = 0; uniform int minimum_alpha_percent< string label = "Minimum alpha percent"; string widget_type = "slider"; From 914d732ccceaa84b8a48a41dc098ce9329c421f4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:50 +0000 Subject: [PATCH 015/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Get-OBSWalkingDeadPixelFixerShader.ps1 | 379 ++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 Commands/Shaders/Get-OBSWalkingDeadPixelFixerShader.ps1 diff --git a/Commands/Shaders/Get-OBSWalkingDeadPixelFixerShader.ps1 b/Commands/Shaders/Get-OBSWalkingDeadPixelFixerShader.ps1 new file mode 100644 index 00000000..bb51914d --- /dev/null +++ b/Commands/Shaders/Get-OBSWalkingDeadPixelFixerShader.ps1 @@ -0,0 +1,379 @@ +function Get-OBSWalkingDeadPixelFixerShader { + +[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] +param( +# Set the Scan_Width of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Width')] +[ComponentModel.DefaultBindingProperty('Scan_Width')] +[Int32] +$ScanWidth, +# Set the Scan_Height of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Height')] +[ComponentModel.DefaultBindingProperty('Scan_Height')] +[Int32] +$ScanHeight, +# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_X')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] +[Int32] +$ScanOffsetX, +# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] +[Int32] +$ScanOffsetY, +# Set the Show_Border of OBSWalkingDeadPixelFixerShader +[Alias('Show_Border')] +[ComponentModel.DefaultBindingProperty('Show_Border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader +[Alias('Contrast_Threshold')] +[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] +[Single] +$ContrastThreshold, +# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Min_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] +[Int32] +$MinClusterSize, +# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Max_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] +[Int32] +$MaxClusterSize, +# Set the Show_Green of OBSWalkingDeadPixelFixerShader +[Alias('Show_Green')] +[ComponentModel.DefaultBindingProperty('Show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the Bypass of OBSWalkingDeadPixelFixerShader +[ComponentModel.DefaultBindingProperty('Bypass')] +[Management.Automation.SwitchParameter] +$Bypass, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'walking-dead-pixel-fixer' +$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter +// by Eegee http://github.com/eegee/ +// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixels located in the user configured scan area and passing the threshold settings will have its colors +// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. +// +// Changelog: +// 0.01 - Initial release +// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. + +uniform int Scan_Width< + string label = "Scan area width"; + int minimum = 1; + int maximum = 2560; + int step = 1; +> = 75; + +uniform int Scan_Height< + string label = "Scan area height"; + int minimum = 1; + int maximum = 1440; + int step = 1; +> = 120; + +uniform int Scan_Offset_X< + string label = "Scan area offset X"; + int minimum = 0; + int maximum = 2560; + int step = 1; +> = 110; + +uniform int Scan_Offset_Y< + string label = "Scan area offset Y"; + int minimum = 0; + int maximum = 1440; + int step = 1; +> = 20; + +uniform bool Show_Border< + string label = "Show scan area border in red"; + string widget_type = "checkbox"; +> = true; + +uniform float Contrast_Threshold< + string label = "Contrast threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; + +uniform int Min_Cluster_Size< + string label = "Min cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 324; + +uniform int Max_Cluster_Size< + string label = "Max cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 400; + +uniform bool Show_Green< + string label = "Show matches in green"; + string widget_type = "checkbox"; +> = true; + +uniform bool Bypass< + string label = "Bypass"; + string widget_type = "checkbox"; +> = false; + + +#define SAMPLE_RADIUS 9 + +float luminance(float3 color) +{ + return dot(color, float3(0.299, 0.587, 0.114)); +} + +void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) +{ + float3 sumColor = float3(0.0, 0.0, 0.0); + float weightSum = 0.0; + contrastCount = 0; + + float3 centerColor = image.Sample(textureSampler, uv).rgb; + float centerLum = luminance(centerColor); + + for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) + { + for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) + { + if (x == 0 && y == 0) continue; + + float2 offset = float2(x, y) / uv_size; + float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); + + // skip central pixel + if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && + ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) + continue; + + float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; + float lum = luminance(sampleColor); + + float weight = 1.0; + sumColor += sampleColor * weight; + weightSum += weight; + + if (abs(lum - centerLum) >= contrastThreshold) + contrastCount++; + } + } + + if (weightSum > 0) + { + avgColor = sumColor / weightSum; + } + else + { + avgColor = centerColor; + } + avgLuminance = luminance(avgColor); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; + + float4 tex = image.Sample(textureSampler, uv); + float3 color = tex.rgb; + + if (!Bypass) + { + int pixX = (int)round(pos.x); + int pixY = (int)round(pos.y); + + int borderwidth = 2; + + bool insideScan = + (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && + (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); + + bool borderingScan = + (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && + (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); + + if (insideScan) + { + float3 avgColor; + float avgLum; + int contrastCount; + + sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); + + if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) + { + color = avgColor; + + if (Show_Green) + { + int left = pixX - borderwidth; + int right = pixX + borderwidth; + int top = pixY - borderwidth; + int bottom = pixY + borderwidth; + + bool onOutline = + abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || + abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; + + if (onOutline) + return float4(0.0, 1.0, 0.0, 1.0); + } + } + } + else if (Show_Border && borderingScan) + { + return float4(1.0, 0.0, 0.0, 0.5); + } + } + return float4(color, tex.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 4e117eae78c4ec7e6848896771cd2cd97ca9ac60 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:50 +0000 Subject: [PATCH 016/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- Commands/Shaders/Get-OBSZoomShader.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/Shaders/Get-OBSZoomShader.ps1 b/Commands/Shaders/Get-OBSZoomShader.ps1 index fa9ba836..bee64743 100644 --- a/Commands/Shaders/Get-OBSZoomShader.ps1 +++ b/Commands/Shaders/Get-OBSZoomShader.ps1 @@ -70,7 +70,7 @@ uniform float power< float minimum = 0; float maximum = 20.0; float step = 0.001; -> = 1.75; +> = 1.0; float4 mainImage(VertData v_in) : TARGET { From f02b1ce46b25cc1e3dbcb72ea587a3b1cd8a19b4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:50 +0000 Subject: [PATCH 017/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- en-us/obs-powershell-commands.help.txt | 354 ++++++++++++------------- 1 file changed, 177 insertions(+), 177 deletions(-) diff --git a/en-us/obs-powershell-commands.help.txt b/en-us/obs-powershell-commands.help.txt index 45ae5b97..da1a78af 100644 --- a/en-us/obs-powershell-commands.help.txt +++ b/en-us/obs-powershell-commands.help.txt @@ -12,19 +12,19 @@ Functions ========= -|Name |Synopsis| -|------------------------------------------------------------------------------------------|--------| -|[Add-OBSInput](docs/Add-OBSInput.md) | -|[Add-OBSProfile](docs/Add-OBSProfile.md) | -|[Add-OBSScene](docs/Add-OBSScene.md) | -|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) | -|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) | -|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) | -|[Clear-OBSScene](docs/Clear-OBSScene.md) | -|[Connect-OBS](docs/Connect-OBS.md) | -|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) | -|[Disconnect-OBS](docs/Disconnect-OBS.md) | -|[Get-OBS](docs/Get-OBS.md) | +|Name |Synopsis | +|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| +|[Add-OBSInput](docs/Add-OBSInput.md) |Add-OBSInput : CreateInput | +|[Add-OBSProfile](docs/Add-OBSProfile.md) |Add-OBSProfile : CreateProfile | +|[Add-OBSScene](docs/Add-OBSScene.md) |Add-OBSScene : CreateScene | +|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) |Add-OBSSceneCollection : CreateSceneCollection | +|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) |Add-OBSSceneItem : CreateSceneItem | +|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) |Add-OBSSourceFilter : CreateSourceFilter | +|[Clear-OBSScene](docs/Clear-OBSScene.md) |Clears a Scene in OBS | +|[Connect-OBS](docs/Connect-OBS.md) |Connects to Open Broadcast Studio | +|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | +|[Disconnect-OBS](docs/Disconnect-OBS.md) |Disconnects OBS | +|[Get-OBS](docs/Get-OBS.md) |Gets OBS | |[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](docs/Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | @@ -53,10 +53,10 @@ Functions |[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) | -|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) | -|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) | -|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | +|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | +|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | +|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) |Get-OBSCurrentSceneTransitionCursor : GetCurrentSceneTransitionCursor | |[Get-OBSCurveShader](docs/Get-OBSCurveShader.md) | |[Get-OBSCutRectPerCornerShader](docs/Get-OBSCutRectPerCornerShader.md) | |[Get-OBSCylinderShader](docs/Get-OBSCylinderShader.md) | @@ -72,7 +72,7 @@ Functions |[Get-OBSDrunkShader](docs/Get-OBSDrunkShader.md) | |[Get-OBSDynamicMaskShader](docs/Get-OBSDynamicMaskShader.md) | |[Get-OBSEdgeDetectionShader](docs/Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](docs/Get-OBSEffect.md) | +|[Get-OBSEffect](docs/Get-OBSEffect.md) |Gets OBS Effects | |[Get-OBSEmbersShader](docs/Get-OBSEmbersShader.md) | |[Get-OBSEmbossColorShader](docs/Get-OBSEmbossColorShader.md) | |[Get-OBSEmbossShader](docs/Get-OBSEmbossShader.md) | @@ -103,61 +103,61 @@ Functions |[Get-OBSGlitchShader](docs/Get-OBSGlitchShader.md) | |[Get-OBSGlowShader](docs/Get-OBSGlowShader.md) | |[Get-OBSGradientShader](docs/Get-OBSGradientShader.md) | -|[Get-OBSGroup](docs/Get-OBSGroup.md) | -|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) | +|[Get-OBSGroup](docs/Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | +|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | |[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | |[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](docs/Get-OBSHotkey.md) | +|[Get-OBSHotkey](docs/Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | |[Get-OBSHslHsvSaturationShader](docs/Get-OBSHslHsvSaturationShader.md) | |[Get-OBSHueRotatonShader](docs/Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](docs/Get-OBSInput.md) | -|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) | -|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) | -|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) | -|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) | -|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) | -|[Get-OBSInputKind](docs/Get-OBSInputKind.md) | -|[Get-OBSInputMute](docs/Get-OBSInputMute.md) | -|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md)| -|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) | -|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) | +|[Get-OBSInput](docs/Get-OBSInput.md) |Get-OBSInput : GetInputList | +|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) |Get-OBSInputAudioBalance : GetInputAudioBalance | +|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) |Get-OBSInputAudioMonitorType : GetInputAudioMonitorType | +|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) |Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset | +|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) |Get-OBSInputAudioTracks : GetInputAudioTracks | +|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) |Get-OBSInputDefaultSettings : GetInputDefaultSettings | +|[Get-OBSInputKind](docs/Get-OBSInputKind.md) |Get-OBSInputKind : GetInputKindList | +|[Get-OBSInputMute](docs/Get-OBSInputMute.md) |Get-OBSInputMute : GetInputMute | +|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md)|Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems| +|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) |Get-OBSInputSettings : GetInputSettings | +|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) |Get-OBSInputVolume : GetInputVolume | |[Get-OBSIntensityScopeShader](docs/Get-OBSIntensityScopeShader.md) | |[Get-OBSInvertLumaShader](docs/Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) |Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay | |[Get-OBSLuminance2Shader](docs/Get-OBSLuminance2Shader.md) | |[Get-OBSLuminanceAlphaShader](docs/Get-OBSLuminanceAlphaShader.md) | |[Get-OBSLuminanceShader](docs/Get-OBSLuminanceShader.md) | |[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) | -|[Get-OBSMonitor](docs/Get-OBSMonitor.md) | +|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | +|[Get-OBSMonitor](docs/Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | |[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | -|[Get-OBSOutput](docs/Get-OBSOutput.md) | -|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) | -|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) | +|[Get-OBSOutput](docs/Get-OBSOutput.md) |Get-OBSOutput : GetOutputList | +|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) |Get-OBSOutputSettings : GetOutputSettings | +|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) |Get-OBSOutputStatus : GetOutputStatus | |[Get-OBSPagePeelShader](docs/Get-OBSPagePeelShader.md) | |[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) | +|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | |[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | |[Get-OBSPolarShader](docs/Get-OBSPolarShader.md) | -|[Get-OBSProfile](docs/Get-OBSProfile.md) | -|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) | +|[Get-OBSProfile](docs/Get-OBSProfile.md) |Get-OBSProfile : GetProfileList | +|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) |Get-OBSProfileParameter : GetProfileParameter | |[Get-OBSPulseShader](docs/Get-OBSPulseShader.md) | |[Get-OBSRainbowShader](docs/Get-OBSRainbowShader.md) | |[Get-OBSRainWindowShader](docs/Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) | -|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) | +|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) |Get-OBSRecordDirectory : GetRecordDirectory | +|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) |Get-OBSRecordStatus : GetRecordStatus | |[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | |[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) | +|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | |[Get-OBSRGBAPercentShader](docs/Get-OBSRGBAPercentShader.md) | |[Get-OBSRgbColorWheelShader](docs/Get-OBSRgbColorWheelShader.md) | |[Get-OBSRgbSplitShader](docs/Get-OBSRgbSplitShader.md) | @@ -173,18 +173,18 @@ Functions |[Get-OBSRoundedStrokeGradientShader](docs/Get-OBSRoundedStrokeGradientShader.md) | |[Get-OBSRoundedStrokeShader](docs/Get-OBSRoundedStrokeShader.md) | |[Get-OBSScanLineShader](docs/Get-OBSScanLineShader.md) | -|[Get-OBSScene](docs/Get-OBSScene.md) | -|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) | -|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) | -|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) | -|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) | -|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) | -|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) | -|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) | -|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) | -|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) | -|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) | -|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) | +|[Get-OBSScene](docs/Get-OBSScene.md) |Get-OBSScene : GetSceneList | +|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) |Get-OBSSceneCollection : GetSceneCollectionList | +|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) |Get-OBSSceneItem : GetSceneItemList | +|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) |Get-OBSSceneItemBlendMode : GetSceneItemBlendMode | +|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) |Get-OBSSceneItemEnabled : GetSceneItemEnabled | +|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) |Get-OBSSceneItemId : GetSceneItemId | +|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) |Get-OBSSceneItemIndex : GetSceneItemIndex | +|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) |Get-OBSSceneItemLocked : GetSceneItemLocked | +|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) |Get-OBSSceneItemSource : GetSceneItemSource | +|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) |Get-OBSSceneItemTransform : GetSceneItemTransform | +|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) |Get-OBSSceneSceneTransitionOverride : GetSceneSceneTransitionOverride | +|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) |Get-OBSSceneTransition : GetSceneTransitionList | |[Get-OBSSeascapeShader](docs/Get-OBSSeascapeShader.md) | |[Get-OBSSeasickShader](docs/Get-OBSSeasickShader.md) | |[Get-OBSSelectiveColorShader](docs/Get-OBSSelectiveColorShader.md) | @@ -193,143 +193,143 @@ Functions |[Get-OBSSimpleGradientShader](docs/Get-OBSSimpleGradientShader.md) | |[Get-OBSSimplexNoiseShader](docs/Get-OBSSimplexNoiseShader.md) | |[Get-OBSSmartDenoiseShader](docs/Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) | -|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) | -|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) | -|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) | -|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) | -|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) | -|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) | +|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) |Get-OBSSourceActive : GetSourceActive | +|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) |Get-OBSSourceFilter : GetSourceFilter | +|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) |Get-OBSSourceFilterDefaultSettings : GetSourceFilterDefaultSettings | +|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) |Get-OBSSourceFilterKind : GetSourceFilterKindList | +|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) |Get-OBSSourceFilterList : GetSourceFilterList | +|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) |Get-OBSSourceScreenshot : GetSourceScreenshot | +|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) |Get-OBSSpecialInputs : GetSpecialInputs | |[Get-OBSSpecularShineShader](docs/Get-OBSSpecularShineShader.md) | |[Get-OBSSpotlightShader](docs/Get-OBSSpotlightShader.md) | -|[Get-OBSStats](docs/Get-OBSStats.md) | -|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) | -|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) | -|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) | +|[Get-OBSStats](docs/Get-OBSStats.md) |Get-OBSStats : GetStats | +|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) |Get-OBSStreamServiceSettings : GetStreamServiceSettings | +|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) |Get-OBSStreamStatus : GetStreamStatus | +|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) |Get-OBSStudioModeEnabled : GetStudioModeEnabled | |[Get-OBSSwirlShader](docs/Get-OBSSwirlShader.md) | |[Get-OBSTetraShader](docs/Get-OBSTetraShader.md) | |[Get-OBSThermalShader](docs/Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) | +|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) |Get-OBSTransitionKind : GetTransitionKindList | |[Get-OBSTvCrtSubpixelShader](docs/Get-OBSTvCrtSubpixelShader.md) | |[Get-OBSTwistShader](docs/Get-OBSTwistShader.md) | |[Get-OBSTwoPassDropShadowShader](docs/Get-OBSTwoPassDropShadowShader.md) | |[Get-OBSVCRShader](docs/Get-OBSVCRShader.md) | -|[Get-OBSVersion](docs/Get-OBSVersion.md) | +|[Get-OBSVersion](docs/Get-OBSVersion.md) |Get-OBSVersion : GetVersion | |[Get-OBSVHSShader](docs/Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) | +|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) |Get-OBSVideoSettings : GetVideoSettings | |[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) | +|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | |[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | |[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | |[Get-OBSZoomXYShader](docs/Get-OBSZoomXYShader.md) | -|[Hide-OBS](docs/Hide-OBS.md) | -|[Import-OBSEffect](docs/Import-OBSEffect.md) | -|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) | -|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) | -|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) | -|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) | -|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) | -|[Receive-OBS](docs/Receive-OBS.md) | -|[Remove-OBS](docs/Remove-OBS.md) | -|[Remove-OBSEffect](docs/Remove-OBSEffect.md) | -|[Remove-OBSInput](docs/Remove-OBSInput.md) | -|[Remove-OBSProfile](docs/Remove-OBSProfile.md) | -|[Remove-OBSScene](docs/Remove-OBSScene.md) | -|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) | -|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) | -|[Resume-OBSRecord](docs/Resume-OBSRecord.md) | -|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) | -|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) | -|[Send-OBS](docs/Send-OBS.md) | -|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) | -|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) | -|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) | -|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) | -|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) | -|[Send-OBSSleep](docs/Send-OBSSleep.md) | -|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) | -|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) | -|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) | -|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) | -|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) | -|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) | -|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) | -|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) | -|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) | -|[Set-OBSColorSource](docs/Set-OBSColorSource.md) | -|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) | -|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) | -|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) | -|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) | -|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) | -|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) | -|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) | -|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) | -|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) | -|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) | -|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) | -|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) | -|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) | -|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) | -|[Set-OBSInputMute](docs/Set-OBSInputMute.md) | -|[Set-OBSInputName](docs/Set-OBSInputName.md) | -|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) | -|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) | -|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) | -|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) | -|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) | -|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) | -|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) | -|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) | -|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) | -|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) | -|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) | -|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) | -|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) | -|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) | -|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) | -|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) | -|[Set-OBSSceneName](docs/Set-OBSSceneName.md) | -|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) | -|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) | -|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) | -|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) | -|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) | -|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) | -|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) | -|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) | -|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) | -|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) | -|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) | -|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) | -|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) | -|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) | -|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) | -|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | -|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | -|[Show-OBS](docs/Show-OBS.md) | -|[Start-OBSEffect](docs/Start-OBSEffect.md) | -|[Start-OBSOutput](docs/Start-OBSOutput.md) | -|[Start-OBSRecord](docs/Start-OBSRecord.md) | -|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | -|[Start-OBSStream](docs/Start-OBSStream.md) | -|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | -|[Stop-OBSEffect](docs/Stop-OBSEffect.md) | -|[Stop-OBSOutput](docs/Stop-OBSOutput.md) | -|[Stop-OBSRecord](docs/Stop-OBSRecord.md) | -|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) | -|[Stop-OBSStream](docs/Stop-OBSStream.md) | -|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) | -|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) | -|[Switch-OBSOutput](docs/Switch-OBSOutput.md) | -|[Switch-OBSRecord](docs/Switch-OBSRecord.md) | -|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) | -|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) | -|[Switch-OBSStream](docs/Switch-OBSStream.md) | -|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) | -|[Watch-OBS](docs/Watch-OBS.md) | +|[Hide-OBS](docs/Hide-OBS.md) |Hide OBS | +|[Import-OBSEffect](docs/Import-OBSEffect.md) |Imports Effects | +|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) |Open-OBSInputFiltersDialog : OpenInputFiltersDialog | +|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) |Open-OBSInputInteractDialog : OpenInputInteractDialog | +|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) |Open-OBSInputPropertiesDialog : OpenInputPropertiesDialog | +|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) |Open-OBSSourceProjector : OpenSourceProjector | +|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) |Open-OBSVideoMixProjector : OpenVideoMixProjector | +|[Receive-OBS](docs/Receive-OBS.md) |Receives data from OBS | +|[Remove-OBS](docs/Remove-OBS.md) |Remove OBS | +|[Remove-OBSEffect](docs/Remove-OBSEffect.md) |Removes OBS Effects | +|[Remove-OBSInput](docs/Remove-OBSInput.md) |Remove-OBSInput : RemoveInput | +|[Remove-OBSProfile](docs/Remove-OBSProfile.md) |Remove-OBSProfile : RemoveProfile | +|[Remove-OBSScene](docs/Remove-OBSScene.md) |Remove-OBSScene : RemoveScene | +|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) |Remove-OBSSceneItem : RemoveSceneItem | +|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) |Remove-OBSSourceFilter : RemoveSourceFilter | +|[Resume-OBSRecord](docs/Resume-OBSRecord.md) |Resume-OBSRecord : ResumeRecord | +|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) |Save-OBSReplayBuffer : SaveReplayBuffer | +|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) |Save-OBSSourceScreenshot : SaveSourceScreenshot | +|[Send-OBS](docs/Send-OBS.md) |Sends messages to the OBS websocket. | +|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) |Send-OBSCallVendorRequest : CallVendorRequest | +|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) |Send-OBSCustomEvent : BroadcastCustomEvent | +|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) |Send-OBSOffsetMediaInputCursor : OffsetMediaInputCursor | +|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) |Send-OBSPauseRecord : PauseRecord | +|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) |Send-OBSPressInputPropertiesButton : PressInputPropertiesButton | +|[Send-OBSSleep](docs/Send-OBSSleep.md) |Send-OBSSleep : Sleep | +|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) |Send-OBSStreamCaption : SendStreamCaption | +|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) |Send-OBSTriggerHotkeyByKeySequence : TriggerHotkeyByKeySequence | +|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) |Send-OBSTriggerHotkeyByName : TriggerHotkeyByName | +|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) |Send-OBSTriggerMediaInputAction : TriggerMediaInputAction | +|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) |Send-OBSTriggerStudioModeTransition : TriggerStudioModeTransition | +|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) |Sets an OBS 3D Filter. | +|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) |Adds or sets an audio output source | +|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) |Sets a browser source | +|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) |Sets a color filter | +|[Set-OBSColorSource](docs/Set-OBSColorSource.md) |Adds a color source | +|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) |Set-OBSCurrentPreviewScene : SetCurrentPreviewScene | +|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) |Set-OBSCurrentProfile : SetCurrentProfile | +|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) |Set-OBSCurrentProgramScene : SetCurrentProgramScene | +|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) |Set-OBSCurrentSceneCollection : SetCurrentSceneCollection | +|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) |Set-OBSCurrentSceneTransition : SetCurrentSceneTransition | +|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) |Set-OBSCurrentSceneTransitionDuration : SetCurrentSceneTransitionDuration | +|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) |Set-OBSCurrentSceneTransitionSettings : SetCurrentSceneTransitionSettings | +|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) |Adds a display source | +|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) |Sets a Equalizer filter. | +|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) |Sets a Gain filter. | +|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) |Set-OBSInputAudioBalance : SetInputAudioBalance | +|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) |Set-OBSInputAudioMonitorType : SetInputAudioMonitorType | +|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) |Set-OBSInputAudioSyncOffset : SetInputAudioSyncOffset | +|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) |Set-OBSInputAudioTracks : SetInputAudioTracks | +|[Set-OBSInputMute](docs/Set-OBSInputMute.md) |Set-OBSInputMute : SetInputMute | +|[Set-OBSInputName](docs/Set-OBSInputName.md) |Set-OBSInputName : SetInputName | +|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) |Set-OBSInputSettings : SetInputSettings | +|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) |Set-OBSInputVolume : SetInputVolume | +|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) |Sets a markdown source | +|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) |Set-OBSMediaInputCursor : SetMediaInputCursor | +|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) |Adds a media source | +|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) |Set-OBSOutputSettings : SetOutputSettings | +|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) |Set-OBSPersistentData : SetPersistentData | +|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) |Set-OBSProfileParameter : SetProfileParameter | +|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) |Set-OBSRecordDirectory : SetRecordDirectory | +|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) |Sets a RenderDelay filter. | +|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) |Sets a Scale filter. | +|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) |Set-OBSSceneItemBlendMode : SetSceneItemBlendMode | +|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) |Set-OBSSceneItemEnabled : SetSceneItemEnabled | +|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) |Set-OBSSceneItemIndex : SetSceneItemIndex | +|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) |Set-OBSSceneItemLocked : SetSceneItemLocked | +|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) |Set-OBSSceneItemTransform : SetSceneItemTransform | +|[Set-OBSSceneName](docs/Set-OBSSceneName.md) |Set-OBSSceneName : SetSceneName | +|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) |Set-OBSSceneSceneTransitionOverride : SetSceneSceneTransitionOverride | +|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) |Sets a scroll filter. | +|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) |Sets a Shader filter. | +|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) |Sets a Sharpness filter. | +|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) |Sets a Sound Cloud Source | +|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) |Set-OBSSourceFilterEnabled : SetSourceFilterEnabled | +|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) |Set-OBSSourceFilterIndex : SetSourceFilterIndex | +|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) |Set-OBSSourceFilterName : SetSourceFilterName | +|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) |Set-OBSSourceFilterSettings : SetSourceFilterSettings | +|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) |Set-OBSStreamServiceSettings : SetStreamServiceSettings | +|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) |Set-OBSStudioModeEnabled : SetStudioModeEnabled | +|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) |Adds a VLC playlist source | +|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) |Set-OBSTBarPosition : SetTBarPosition | +|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) |Set-OBSVideoSettings : SetVideoSettings | +|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) |Adds a VLC playlist source | +|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) |OBS Waveform Source | +|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) |Adds or sets a window capture source | +|[Show-OBS](docs/Show-OBS.md) |Shows content in OBS | +|[Start-OBSEffect](docs/Start-OBSEffect.md) |Starts obs-powershell effects. | +|[Start-OBSOutput](docs/Start-OBSOutput.md) |Start-OBSOutput : StartOutput | +|[Start-OBSRecord](docs/Start-OBSRecord.md) |Start-OBSRecord : StartRecord | +|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) |Start-OBSReplayBuffer : StartReplayBuffer | +|[Start-OBSStream](docs/Start-OBSStream.md) |Start-OBSStream : StartStream | +|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) |Start-OBSVirtualCam : StartVirtualCam | +|[Stop-OBSEffect](docs/Stop-OBSEffect.md) |Stops obs-powershell effects. | +|[Stop-OBSOutput](docs/Stop-OBSOutput.md) |Stop-OBSOutput : StopOutput | +|[Stop-OBSRecord](docs/Stop-OBSRecord.md) |Stop-OBSRecord : StopRecord | +|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) |Stop-OBSReplayBuffer : StopReplayBuffer | +|[Stop-OBSStream](docs/Stop-OBSStream.md) |Stop-OBSStream : StopStream | +|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) |Stop-OBSVirtualCam : StopVirtualCam | +|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) |Switch-OBSInputMute : ToggleInputMute | +|[Switch-OBSOutput](docs/Switch-OBSOutput.md) |Switch-OBSOutput : ToggleOutput | +|[Switch-OBSRecord](docs/Switch-OBSRecord.md) |Switch-OBSRecord : ToggleRecord | +|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) |Switch-OBSRecordPause : ToggleRecordPause | +|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) |Switch-OBSReplayBuffer : ToggleReplayBuffer | +|[Switch-OBSStream](docs/Switch-OBSStream.md) |Switch-OBSStream : ToggleStream | +|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) |Switch-OBSVirtualCam : ToggleVirtualCam | +|[Watch-OBS](docs/Watch-OBS.md) |Watches OBS | From 1f1ab28f0373ff8d5348541b256ebec8f14b9e8d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:51:50 +0000 Subject: [PATCH 018/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- obs-powershell.psd1 | 321 ++++++++++++++++++++++---------------------- 1 file changed, 164 insertions(+), 157 deletions(-) diff --git a/obs-powershell.psd1 b/obs-powershell.psd1 index cc336277..a47a2bcc 100644 --- a/obs-powershell.psd1 +++ b/obs-powershell.psd1 @@ -104,158 +104,11 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Send-OBS', 'Show-OBS', 'Watch-OBS', -'Set-OBSAudioOutputSource', -'Set-OBSBrowserSource', -'Set-OBSColorSource', -'Set-OBSDisplaySource', -'Set-OBSMarkdownSource', -'Set-OBSMediaSource', -'Set-OBSSoundCloudSource', -'Set-OBSSwitchSource', -'Set-OBSVLCSource', -'Set-OBSWaveformSource', -'Set-OBSWindowSource', -'Get-OBS3dSwapTransitionShader', -'Get-OBSAddShader', -'Get-OBSAlphaBorderShader', -'Get-OBSAlphaGamingBentCameraShader', -'Get-OBSAnimatedPathShader', -'Get-OBSAnimatedTextureShader', -'Get-OBSAsciiShader', -'Get-OBSAspectRatioShader', -'Get-OBSBackgroundRemovalShader', -'Get-OBSBlendOpacityShader', -'Get-OBSBlinkShader', -'Get-OBSBloomShader', -'Get-OBSBorderShader', -'Get-OBSBoxBlurShader', -'Get-OBSBulgePinchShader', -'Get-OBSBurnShader', -'Get-OBSCartoonShader', -'Get-OBSCellShadedShader', -'Get-OBSChromaticAberrationShader', -'Get-OBSChromaUVDistortionShader', -'Get-OBSCircleMaskFilterShader', -'Get-OBSClockAnalogShader', -'Get-OBSClockDigitalLedShader', -'Get-OBSClockDigitalNixieShader', -'Get-OBSColorDepthShader', -'Get-OBSColorGradeFilterShader', -'Get-OBSCornerPinShader', -'Get-OBSCrtCurvatureShader', -'Get-OBSCurveShader', -'Get-OBSCutRectPerCornerShader', -'Get-OBSCylinderShader', -'Get-OBSDarkenShader', -'Get-OBSDeadPixelFixerShader', -'Get-OBSDensitySatHueShader', -'Get-OBSDiffuseTransitionShader', -'Get-OBSDigitalRainShader', -'Get-OBSDivideRotateShader', -'Get-OBSDoodleShader', -'Get-OBSDrawingsShader', -'Get-OBSDropShadowShader', -'Get-OBSDrunkShader', -'Get-OBSDynamicMaskShader', -'Get-OBSEdgeDetectionShader', -'Get-OBSEmbersShader', -'Get-OBSEmbossColorShader', -'Get-OBSEmbossShader', -'Get-OBSExeldroBentCameraShader', -'Get-OBSFadeTransitionShader', -'Get-OBSFillColorGradientShader', -'Get-OBSFillColorLinearShader', -'Get-OBSFillColorRadialDegreesShader', -'Get-OBSFillColorRadialPercentageShader', -'Get-OBSFilterTemplateShader', -'Get-OBSFire3Shader', -'Get-OBSFireShader', -'Get-OBSFireworks2Shader', -'Get-OBSFireworksShader', -'Get-OBSFisheyeShader', -'Get-OBSFisheyeXyShader', -'Get-OBSFlipShader', -'Get-OBSFrostedGlassShader', -'Get-OBSGammaCorrectionShader', -'Get-OBSGaussianBlurAdvancedShader', -'Get-OBSGaussianBlurShader', -'Get-OBSGaussianBlurSimpleShader', -'Get-OBSGaussianExampleShader', -'Get-OBSGaussianSimpleShader', -'Get-OBSGbCameraShader', -'Get-OBSGlassShader', -'Get-OBSGlitchAnalogShader', -'Get-OBSGlitchShader', -'Get-OBSGlowShader', -'Get-OBSGradientShader', -'Get-OBSHalftoneShader', -'Get-OBSHeatWaveSimpleShader', -'Get-OBSHexagonShader', -'Get-OBSHslHsvSaturationShader', -'Get-OBSHueRotatonShader', -'Get-OBSIntensityScopeShader', -'Get-OBSInvertLumaShader', -'Get-OBSLuminance2Shader', -'Get-OBSLuminanceAlphaShader', -'Get-OBSLuminanceShader', -'Get-OBSMatrixShader', -'Get-OBSMultiplyShader', -'Get-OBSNightSkyShader', -'Get-OBSOpacityShader', -'Get-OBSPagePeelShader', -'Get-OBSPagePeelTransitionShader', -'Get-OBSPerlinNoiseShader', -'Get-OBSPieChartShader', -'Get-OBSPixelationShader', -'Get-OBSPixelationTransitionShader', -'Get-OBSPolarShader', -'Get-OBSPulseShader', -'Get-OBSRainbowShader', -'Get-OBSRainWindowShader', -'Get-OBSRectangularDropShadowShader', -'Get-OBSReflectShader', -'Get-OBSRemovePartialPixelsShader', -'Get-OBSRepeatShader', -'Get-OBSRepeatTextureShader', -'Get-OBSRGBAPercentShader', -'Get-OBSRgbColorWheelShader', -'Get-OBSRgbSplitShader', -'Get-OBSRgbvisibilityShader', -'Get-OBSRGSSAAShader', -'Get-OBSRippleShader', -'Get-OBSRotatingSourceShader', -'Get-OBSRotatoeShader', -'Get-OBSRoundedRect2Shader', -'Get-OBSRoundedRectPerCornerShader', -'Get-OBSRoundedRectPerSideShader', -'Get-OBSRoundedRectShader', -'Get-OBSRoundedStrokeGradientShader', -'Get-OBSRoundedStrokeShader', -'Get-OBSScanLineShader', -'Get-OBSSeascapeShader', -'Get-OBSSeasickShader', -'Get-OBSSelectiveColorShader', -'Get-OBSShakeShader', -'Get-OBSShineShader', -'Get-OBSSimpleGradientShader', -'Get-OBSSimplexNoiseShader', -'Get-OBSSmartDenoiseShader', -'Get-OBSSpecularShineShader', -'Get-OBSSpotlightShader', -'Get-OBSSwirlShader', -'Get-OBSTetraShader', -'Get-OBSThermalShader', -'Get-OBSTvCrtSubpixelShader', -'Get-OBSTwistShader', -'Get-OBSTwoPassDropShadowShader', -'Get-OBSVCRShader', -'Get-OBSVHSShader', -'Get-OBSVignettingShader', -'Get-OBSVoronoiPixelationShader', -'Get-OBSZigZagShader', -'Get-OBSZoomBlurShader', -'Get-OBSZoomShader', -'Get-OBSZoomXYShader', +'Get-OBSEffect', +'Import-OBSEffect', +'Remove-OBSEffect', +'Start-OBSEffect', +'Stop-OBSEffect', 'Set-OBS3DFilter', 'Set-OBSColorFilter', 'Set-OBSEqualizerFilter', @@ -405,10 +258,164 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Switch-OBSReplayBuffer', 'Switch-OBSStream', 'Switch-OBSVirtualCam', -'Get-OBSEffect', -'Import-OBSEffect', -'Remove-OBSEffect', -'Start-OBSEffect', -'Stop-OBSEffect' +'Get-OBS3dPanelShader', +'Get-OBS3dSwapTransitionShader', +'Get-OBSAddShader', +'Get-OBSAlphaBorderShader', +'Get-OBSAlphaGamingBentCameraShader', +'Get-OBSAnimatedPathShader', +'Get-OBSAnimatedTextureShader', +'Get-OBSAsciiShader', +'Get-OBSAspectRatioShader', +'Get-OBSBackgroundRemovalShader', +'Get-OBSBlendOpacityShader', +'Get-OBSBlinkShader', +'Get-OBSBloomShader', +'Get-OBSBorderShader', +'Get-OBSBoxBlurShader', +'Get-OBSBulgePinchShader', +'Get-OBSBurnShader', +'Get-OBSCartoonShader', +'Get-OBSCellShadedShader', +'Get-OBSChromaticAberrationShader', +'Get-OBSChromaUVDistortionShader', +'Get-OBSCircleMaskFilterShader', +'Get-OBSClockAnalogShader', +'Get-OBSClockDigitalLedShader', +'Get-OBSClockDigitalNixieShader', +'Get-OBSColorDepthShader', +'Get-OBSColorGradeFilterShader', +'Get-OBSCornerPinShader', +'Get-OBSCrtCurvatureShader', +'Get-OBSCubeRotatingShader', +'Get-OBSCurveShader', +'Get-OBSCutRectPerCornerShader', +'Get-OBSCylinderShader', +'Get-OBSDarkenShader', +'Get-OBSDeadPixelFixerShader', +'Get-OBSDensitySatHueShader', +'Get-OBSDiffuseTransitionShader', +'Get-OBSDigitalRainShader', +'Get-OBSDivideRotateShader', +'Get-OBSDoodleShader', +'Get-OBSDrawingsShader', +'Get-OBSDropShadowShader', +'Get-OBSDrunkShader', +'Get-OBSDynamicMaskShader', +'Get-OBSEdgeDetectionShader', +'Get-OBSEmbersShader', +'Get-OBSEmbossColorShader', +'Get-OBSEmbossShader', +'Get-OBSExeldroBentCameraShader', +'Get-OBSFadeTransitionShader', +'Get-OBSFillColorGradientShader', +'Get-OBSFillColorLinearShader', +'Get-OBSFillColorRadialDegreesShader', +'Get-OBSFillColorRadialPercentageShader', +'Get-OBSFilterTemplateShader', +'Get-OBSFire3Shader', +'Get-OBSFireShader', +'Get-OBSFireworks2Shader', +'Get-OBSFireworksShader', +'Get-OBSFisheyeShader', +'Get-OBSFisheyeXyShader', +'Get-OBSFlipShader', +'Get-OBSFrostedGlassShader', +'Get-OBSGammaCorrectionShader', +'Get-OBSGaussianBlurAdvancedShader', +'Get-OBSGaussianBlurShader', +'Get-OBSGaussianBlurSimpleShader', +'Get-OBSGaussianExampleShader', +'Get-OBSGaussianSimpleShader', +'Get-OBSGbCameraShader', +'Get-OBSGlassShader', +'Get-OBSGlitchAnalogShader', +'Get-OBSGlitchShader', +'Get-OBSGlowShader', +'Get-OBSGradientShader', +'Get-OBSHalftoneShader', +'Get-OBSHardBlinkShader', +'Get-OBSHeatWaveSimpleShader', +'Get-OBSHexagonShader', +'Get-OBSHslHsvSaturationShader', +'Get-OBSHueRotatonShader', +'Get-OBSIntensityScopeShader', +'Get-OBSInvertLumaShader', +'Get-OBSLuminance2Shader', +'Get-OBSLuminanceAlphaShader', +'Get-OBSLuminanceShader', +'Get-OBSMatrixShader', +'Get-OBSMotionBlurShader', +'Get-OBSMultiplyShader', +'Get-OBSNightSkyShader', +'Get-OBSOpacityShader', +'Get-OBSPagePeelShader', +'Get-OBSPagePeelTransitionShader', +'Get-OBSPerlinNoiseShader', +'Get-OBSPerspectiveShader', +'Get-OBSPieChartShader', +'Get-OBSPixelationShader', +'Get-OBSPixelationTransitionShader', +'Get-OBSPolarShader', +'Get-OBSPulseShader', +'Get-OBSRainbowShader', +'Get-OBSRainWindowShader', +'Get-OBSRectangularDropShadowShader', +'Get-OBSReflectShader', +'Get-OBSRemovePartialPixelsShader', +'Get-OBSRepeatGridCenterCropShader', +'Get-OBSRepeatShader', +'Get-OBSRepeatTextureShader', +'Get-OBSRGBAPercentShader', +'Get-OBSRgbColorWheelShader', +'Get-OBSRgbSplitShader', +'Get-OBSRgbvisibilityShader', +'Get-OBSRGSSAAShader', +'Get-OBSRippleShader', +'Get-OBSRotatingSourceShader', +'Get-OBSRotatoeShader', +'Get-OBSRoundedRect2Shader', +'Get-OBSRoundedRectPerCornerShader', +'Get-OBSRoundedRectPerSideShader', +'Get-OBSRoundedRectShader', +'Get-OBSRoundedStrokeGradientShader', +'Get-OBSRoundedStrokeShader', +'Get-OBSScanLineShader', +'Get-OBSSeascapeShader', +'Get-OBSSeasickShader', +'Get-OBSSelectiveColorShader', +'Get-OBSShakeShader', +'Get-OBSShineShader', +'Get-OBSSimpleGradientShader', +'Get-OBSSimplexNoiseShader', +'Get-OBSSmartDenoiseShader', +'Get-OBSSpecularShineShader', +'Get-OBSSpotlightShader', +'Get-OBSSwirlShader', +'Get-OBSTetraShader', +'Get-OBSThermalShader', +'Get-OBSTvCrtSubpixelShader', +'Get-OBSTwistShader', +'Get-OBSTwoPassDropShadowShader', +'Get-OBSVCRShader', +'Get-OBSVHSShader', +'Get-OBSVignettingShader', +'Get-OBSVoronoiPixelationShader', +'Get-OBSWalkingDeadPixelFixerShader', +'Get-OBSZigZagShader', +'Get-OBSZoomBlurShader', +'Get-OBSZoomShader', +'Get-OBSZoomXYShader', +'Set-OBSAudioOutputSource', +'Set-OBSBrowserSource', +'Set-OBSColorSource', +'Set-OBSDisplaySource', +'Set-OBSMarkdownSource', +'Set-OBSMediaSource', +'Set-OBSSoundCloudSource', +'Set-OBSSwitchSource', +'Set-OBSVLCSource', +'Set-OBSWaveformSource', +'Set-OBSWindowSource' } From ef04dcd799427416b7178a9f881a8912d7a1f553 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:04 +0000 Subject: [PATCH 019/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBS3dPanelShader.md | 156 +++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/Get-OBS3dPanelShader.md diff --git a/docs/Get-OBS3dPanelShader.md b/docs/Get-OBS3dPanelShader.md new file mode 100644 index 00000000..5079fd77 --- /dev/null +++ b/docs/Get-OBS3dPanelShader.md @@ -0,0 +1,156 @@ +Get-OBS3dPanelShader +-------------------- + +### Synopsis + +Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Brightness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |9 |false | + +#### **Credits** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |0 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |13 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **LightPosition** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|--------------| +|`[int]`|false |10 |false |light_position| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PosX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |5 |false |pos_x | + +#### **PosY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |6 |false |pos_y | + +#### **RadiusFb** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |8 |false |radius_fb| + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |14 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |12 |true (ByPropertyName)|SceneItemName| + +#### **Thickness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |7 |false | + +#### **TiltXDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |2 |false |tilt_x_deg| + +#### **TiltYDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |3 |false |tilt_y_deg| + +#### **TiltZDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |4 |false |tilt_z_deg| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Wiggle** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |11 |false | + +#### **WiggleRot** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |wiggle_rot| + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBS3dPanelShader; CommonParameters=True; parameter=System.Object[]}} +``` From c3cfa88b263de1bb828f815a115982a4921e6032 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:04 +0000 Subject: [PATCH 020/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Get-OBS3dPanelShader.json | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBS3dPanelShader.json diff --git a/docs/_data/Help/Get-OBS3dPanelShader.json b/docs/_data/Help/Get-OBS3dPanelShader.json new file mode 100644 index 00000000..fe53f972 --- /dev/null +++ b/docs/_data/Help/Get-OBS3dPanelShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 4baec2467fe985520a34b31db7986900197de3d6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:09 +0000 Subject: [PATCH 021/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSCubeRotatingShader.md | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Get-OBSCubeRotatingShader.md diff --git a/docs/Get-OBSCubeRotatingShader.md b/docs/Get-OBSCubeRotatingShader.md new file mode 100644 index 00000000..5c393585 --- /dev/null +++ b/docs/Get-OBSCubeRotatingShader.md @@ -0,0 +1,114 @@ +Get-OBSCubeRotatingShader +------------------------- + +### Synopsis + +Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |7 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Images** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |0 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OtherImage1** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |3 |false |other_image1| + +#### **OtherImage2** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |other_image2| + +#### **OtherImage3** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |5 |false |other_image3| + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |8 |false |ShaderContent| + +#### **Shadow** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |6 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSCubeRotatingShader; CommonParameters=True; parameter=System.Object[]}} +``` From b1772f997ddadadce3cacc7570ca89e12dd2b19f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:10 +0000 Subject: [PATCH 022/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../_data/Help/Get-OBSCubeRotatingShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSCubeRotatingShader.json diff --git a/docs/_data/Help/Get-OBSCubeRotatingShader.json b/docs/_data/Help/Get-OBSCubeRotatingShader.json new file mode 100644 index 00000000..8d837b62 --- /dev/null +++ b/docs/_data/Help/Get-OBSCubeRotatingShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 66d9214ffaa7661b87997903a29c335a27cd1708 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:17 +0000 Subject: [PATCH 023/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSHardBlinkShader.md | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Get-OBSHardBlinkShader.md diff --git a/docs/Get-OBSHardBlinkShader.md b/docs/Get-OBSHardBlinkShader.md new file mode 100644 index 00000000..9be98bd9 --- /dev/null +++ b/docs/Get-OBSHardBlinkShader.md @@ -0,0 +1,90 @@ +Get-OBSHardBlinkShader +---------------------- + +### Synopsis + +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Timeoff** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **Timeon** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +``` From fe7ba8febbdbababef396a2dff2a8a82de1a3f9a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:17 +0000 Subject: [PATCH 024/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Get-OBSHardBlinkShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSHardBlinkShader.json diff --git a/docs/_data/Help/Get-OBSHardBlinkShader.json b/docs/_data/Help/Get-OBSHardBlinkShader.json new file mode 100644 index 00000000..43665759 --- /dev/null +++ b/docs/_data/Help/Get-OBSHardBlinkShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 031a9548ba8bdca6913fe2fa682a9b72079a7642 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:20 +0000 Subject: [PATCH 025/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSMotionBlurShader.md | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Get-OBSMotionBlurShader.md diff --git a/docs/Get-OBSMotionBlurShader.md b/docs/Get-OBSMotionBlurShader.md new file mode 100644 index 00000000..6788858e --- /dev/null +++ b/docs/Get-OBSMotionBlurShader.md @@ -0,0 +1,90 @@ +Get-OBSMotionBlurShader +----------------------- + +### Synopsis + +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PreviousOutput** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------| +|`[string]`|false |0 |false |previous_output| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +``` From a95209c4beb620fd6da419a79d607d494a0e635d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:20 +0000 Subject: [PATCH 026/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Get-OBSMotionBlurShader.json | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSMotionBlurShader.json diff --git a/docs/_data/Help/Get-OBSMotionBlurShader.json b/docs/_data/Help/Get-OBSMotionBlurShader.json new file mode 100644 index 00000000..eb301da6 --- /dev/null +++ b/docs/_data/Help/Get-OBSMotionBlurShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 8cea6ddafa63376cd9333440693311125a29e1af Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:21 +0000 Subject: [PATCH 027/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSPerspectiveShader.md | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Get-OBSPerspectiveShader.md diff --git a/docs/Get-OBSPerspectiveShader.md b/docs/Get-OBSPerspectiveShader.md new file mode 100644 index 00000000..b39795f2 --- /dev/null +++ b/docs/Get-OBSPerspectiveShader.md @@ -0,0 +1,114 @@ +Get-OBSPerspectiveShader +------------------------ + +### Synopsis + +Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AngleX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |0 |false |angle_x| + +#### **AngleY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |1 |false |angle_y| + +#### **AngleZ** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |2 |false |angle_z| + +#### **BorderColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |border_color| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Perspective** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |show_border| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSPerspectiveShader; CommonParameters=True; parameter=System.Object[]}} +``` From ba4ef9e83daa7bde33cfd656f2ba704fd33322f9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:21 +0000 Subject: [PATCH 028/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Get-OBSPerspectiveShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSPerspectiveShader.json diff --git a/docs/_data/Help/Get-OBSPerspectiveShader.json b/docs/_data/Help/Get-OBSPerspectiveShader.json new file mode 100644 index 00000000..43953a39 --- /dev/null +++ b/docs/_data/Help/Get-OBSPerspectiveShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 5a4bf7b1a5e19a45050e9d35c927c7ec1d678417 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:23 +0000 Subject: [PATCH 029/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSRepeatGridCenterCropShader.md | 108 ++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Get-OBSRepeatGridCenterCropShader.md diff --git a/docs/Get-OBSRepeatGridCenterCropShader.md b/docs/Get-OBSRepeatGridCenterCropShader.md new file mode 100644 index 00000000..bd68baeb --- /dev/null +++ b/docs/Get-OBSRepeatGridCenterCropShader.md @@ -0,0 +1,108 @@ +Get-OBSRepeatGridCenterCropShader +--------------------------------- + +### Synopsis + +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Alpha** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **Columns** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Image** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |1 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Rows** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |4 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ViewProj** + +|Type |Required|Position|PipelineInput| +|-------------|--------|--------|-------------| +|`[float[][]]`|false |0 |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From 6863c7235f20d509b4672262df257a5a33f9e219 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:23 +0000 Subject: [PATCH 030/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Get-OBSRepeatGridCenterCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json diff --git a/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json new file mode 100644 index 00000000..c4ac45ff --- /dev/null +++ b/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From dab04526bef8200bdb72a58f14fbf5f4cfedece3 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:29 +0000 Subject: [PATCH 031/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Get-OBSWalkingDeadPixelFixerShader.md | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/Get-OBSWalkingDeadPixelFixerShader.md diff --git a/docs/Get-OBSWalkingDeadPixelFixerShader.md b/docs/Get-OBSWalkingDeadPixelFixerShader.md new file mode 100644 index 00000000..fb0a4df0 --- /dev/null +++ b/docs/Get-OBSWalkingDeadPixelFixerShader.md @@ -0,0 +1,138 @@ +Get-OBSWalkingDeadPixelFixerShader +---------------------------------- + +### Synopsis + +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Bypass** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ContrastThreshold** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------------| +|`[float]`|false |4 |false |Contrast_Threshold| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |8 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaxClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |6 |false |Max_Cluster_Size| + +#### **MinClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |5 |false |Min_Cluster_Size| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ScanHeight** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-----------| +|`[int]`|false |1 |false |Scan_Height| + +#### **ScanOffsetX** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |2 |false |Scan_Offset_X| + +#### **ScanOffsetY** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |3 |false |Scan_Offset_Y| + +#### **ScanWidth** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------| +|`[int]`|false |0 |false |Scan_Width| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |9 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |Show_Border| + +#### **ShowGreen** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |Show_Green| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +``` From 186d5bd029f6d8769d197fda152f6eddea79e172 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:29 +0000 Subject: [PATCH 032/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Get-OBSWalkingDeadPixelFixerShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json diff --git a/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json new file mode 100644 index 00000000..858f3e99 --- /dev/null +++ b/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 5a2ea264eaf70195fbfed2140740c4ac00ee20f3 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:39 +0000 Subject: [PATCH 033/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBS3dPanelShader.md | 156 +++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/Add-OBS3dPanelShader.md diff --git a/docs/Add-OBS3dPanelShader.md b/docs/Add-OBS3dPanelShader.md new file mode 100644 index 00000000..5079fd77 --- /dev/null +++ b/docs/Add-OBS3dPanelShader.md @@ -0,0 +1,156 @@ +Get-OBS3dPanelShader +-------------------- + +### Synopsis + +Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Brightness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |9 |false | + +#### **Credits** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |0 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |13 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **LightPosition** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|--------------| +|`[int]`|false |10 |false |light_position| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PosX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |5 |false |pos_x | + +#### **PosY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |6 |false |pos_y | + +#### **RadiusFb** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |8 |false |radius_fb| + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |14 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |12 |true (ByPropertyName)|SceneItemName| + +#### **Thickness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |7 |false | + +#### **TiltXDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |2 |false |tilt_x_deg| + +#### **TiltYDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |3 |false |tilt_y_deg| + +#### **TiltZDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |4 |false |tilt_z_deg| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Wiggle** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |11 |false | + +#### **WiggleRot** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |wiggle_rot| + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBS3dPanelShader; CommonParameters=True; parameter=System.Object[]}} +``` From 66aa7b8050d4ec15c3b9f48c0765f8fe60fc2896 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:39 +0000 Subject: [PATCH 034/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Add-OBS3dPanelShader.json | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBS3dPanelShader.json diff --git a/docs/_data/Help/Add-OBS3dPanelShader.json b/docs/_data/Help/Add-OBS3dPanelShader.json new file mode 100644 index 00000000..fe53f972 --- /dev/null +++ b/docs/_data/Help/Add-OBS3dPanelShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 6357acee37443acf2dc41c4cc187672cf4d66347 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:42 +0000 Subject: [PATCH 035/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSCubeRotatingShader.md | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Add-OBSCubeRotatingShader.md diff --git a/docs/Add-OBSCubeRotatingShader.md b/docs/Add-OBSCubeRotatingShader.md new file mode 100644 index 00000000..5c393585 --- /dev/null +++ b/docs/Add-OBSCubeRotatingShader.md @@ -0,0 +1,114 @@ +Get-OBSCubeRotatingShader +------------------------- + +### Synopsis + +Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |7 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Images** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |0 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OtherImage1** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |3 |false |other_image1| + +#### **OtherImage2** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |other_image2| + +#### **OtherImage3** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |5 |false |other_image3| + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |8 |false |ShaderContent| + +#### **Shadow** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |6 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSCubeRotatingShader; CommonParameters=True; parameter=System.Object[]}} +``` From 57a7588bc6bbd1b4655590e79560821bfac91f46 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:42 +0000 Subject: [PATCH 036/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../_data/Help/Add-OBSCubeRotatingShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSCubeRotatingShader.json diff --git a/docs/_data/Help/Add-OBSCubeRotatingShader.json b/docs/_data/Help/Add-OBSCubeRotatingShader.json new file mode 100644 index 00000000..8d837b62 --- /dev/null +++ b/docs/_data/Help/Add-OBSCubeRotatingShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From e100eb37e386103faddc5aac8edca8d0f9c45f3c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:46 +0000 Subject: [PATCH 037/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSHardBlinkShader.md | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Add-OBSHardBlinkShader.md diff --git a/docs/Add-OBSHardBlinkShader.md b/docs/Add-OBSHardBlinkShader.md new file mode 100644 index 00000000..9be98bd9 --- /dev/null +++ b/docs/Add-OBSHardBlinkShader.md @@ -0,0 +1,90 @@ +Get-OBSHardBlinkShader +---------------------- + +### Synopsis + +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Timeoff** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **Timeon** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +``` From 61c6822503e6a0436d8149a62803a050c5b9ac55 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:46 +0000 Subject: [PATCH 038/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Add-OBSHardBlinkShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSHardBlinkShader.json diff --git a/docs/_data/Help/Add-OBSHardBlinkShader.json b/docs/_data/Help/Add-OBSHardBlinkShader.json new file mode 100644 index 00000000..43665759 --- /dev/null +++ b/docs/_data/Help/Add-OBSHardBlinkShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From e71cb8228db99be45c32f4c5bbc8a45833b531e9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:47 +0000 Subject: [PATCH 039/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSMotionBlurShader.md | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Add-OBSMotionBlurShader.md diff --git a/docs/Add-OBSMotionBlurShader.md b/docs/Add-OBSMotionBlurShader.md new file mode 100644 index 00000000..6788858e --- /dev/null +++ b/docs/Add-OBSMotionBlurShader.md @@ -0,0 +1,90 @@ +Get-OBSMotionBlurShader +----------------------- + +### Synopsis + +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PreviousOutput** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------| +|`[string]`|false |0 |false |previous_output| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +``` From 20eccfba69d20c91b66cbb1215986b2aaf086814 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:47 +0000 Subject: [PATCH 040/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Add-OBSMotionBlurShader.json | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSMotionBlurShader.json diff --git a/docs/_data/Help/Add-OBSMotionBlurShader.json b/docs/_data/Help/Add-OBSMotionBlurShader.json new file mode 100644 index 00000000..eb301da6 --- /dev/null +++ b/docs/_data/Help/Add-OBSMotionBlurShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 3ca2f4b87deed2fc08acf79e2aa1ed58ade1fcd6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:48 +0000 Subject: [PATCH 041/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSPerspectiveShader.md | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Add-OBSPerspectiveShader.md diff --git a/docs/Add-OBSPerspectiveShader.md b/docs/Add-OBSPerspectiveShader.md new file mode 100644 index 00000000..b39795f2 --- /dev/null +++ b/docs/Add-OBSPerspectiveShader.md @@ -0,0 +1,114 @@ +Get-OBSPerspectiveShader +------------------------ + +### Synopsis + +Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AngleX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |0 |false |angle_x| + +#### **AngleY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |1 |false |angle_y| + +#### **AngleZ** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |2 |false |angle_z| + +#### **BorderColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |border_color| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Perspective** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |show_border| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSPerspectiveShader; CommonParameters=True; parameter=System.Object[]}} +``` From c7ec7f58e33a67f07f5be141002989f998255aab Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:48 +0000 Subject: [PATCH 042/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Add-OBSPerspectiveShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSPerspectiveShader.json diff --git a/docs/_data/Help/Add-OBSPerspectiveShader.json b/docs/_data/Help/Add-OBSPerspectiveShader.json new file mode 100644 index 00000000..43953a39 --- /dev/null +++ b/docs/_data/Help/Add-OBSPerspectiveShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 99e9c8bcff4eaff43c3693368e734c7f167d2009 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:49 +0000 Subject: [PATCH 043/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSRepeatGridCenterCropShader.md | 108 ++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Add-OBSRepeatGridCenterCropShader.md diff --git a/docs/Add-OBSRepeatGridCenterCropShader.md b/docs/Add-OBSRepeatGridCenterCropShader.md new file mode 100644 index 00000000..bd68baeb --- /dev/null +++ b/docs/Add-OBSRepeatGridCenterCropShader.md @@ -0,0 +1,108 @@ +Get-OBSRepeatGridCenterCropShader +--------------------------------- + +### Synopsis + +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Alpha** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **Columns** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Image** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |1 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Rows** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |4 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ViewProj** + +|Type |Required|Position|PipelineInput| +|-------------|--------|--------|-------------| +|`[float[][]]`|false |0 |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From 69adb55ce7c48b67e6d456f50cb146971363aec2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:49 +0000 Subject: [PATCH 044/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Add-OBSRepeatGridCenterCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json diff --git a/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json new file mode 100644 index 00000000..c4ac45ff --- /dev/null +++ b/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 68789f55ffb444da58092447dfa2ded67fb54a40 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:53 +0000 Subject: [PATCH 045/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Add-OBSWalkingDeadPixelFixerShader.md | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/Add-OBSWalkingDeadPixelFixerShader.md diff --git a/docs/Add-OBSWalkingDeadPixelFixerShader.md b/docs/Add-OBSWalkingDeadPixelFixerShader.md new file mode 100644 index 00000000..fb0a4df0 --- /dev/null +++ b/docs/Add-OBSWalkingDeadPixelFixerShader.md @@ -0,0 +1,138 @@ +Get-OBSWalkingDeadPixelFixerShader +---------------------------------- + +### Synopsis + +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Bypass** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ContrastThreshold** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------------| +|`[float]`|false |4 |false |Contrast_Threshold| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |8 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaxClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |6 |false |Max_Cluster_Size| + +#### **MinClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |5 |false |Min_Cluster_Size| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ScanHeight** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-----------| +|`[int]`|false |1 |false |Scan_Height| + +#### **ScanOffsetX** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |2 |false |Scan_Offset_X| + +#### **ScanOffsetY** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |3 |false |Scan_Offset_Y| + +#### **ScanWidth** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------| +|`[int]`|false |0 |false |Scan_Width| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |9 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |Show_Border| + +#### **ShowGreen** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |Show_Green| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +``` From d7edf1c0846bfd76f3d5af28f97dbb27de04f11a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:52:53 +0000 Subject: [PATCH 046/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Add-OBSWalkingDeadPixelFixerShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json diff --git a/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json new file mode 100644 index 00000000..858f3e99 --- /dev/null +++ b/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 0e285053f17fa59a336558adbffce7b4b6a04df2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:03 +0000 Subject: [PATCH 047/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBS3dPanelShader.md | 156 +++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/Set-OBS3dPanelShader.md diff --git a/docs/Set-OBS3dPanelShader.md b/docs/Set-OBS3dPanelShader.md new file mode 100644 index 00000000..5079fd77 --- /dev/null +++ b/docs/Set-OBS3dPanelShader.md @@ -0,0 +1,156 @@ +Get-OBS3dPanelShader +-------------------- + +### Synopsis + +Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Brightness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |9 |false | + +#### **Credits** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |0 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |13 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **LightPosition** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|--------------| +|`[int]`|false |10 |false |light_position| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PosX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |5 |false |pos_x | + +#### **PosY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |6 |false |pos_y | + +#### **RadiusFb** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |8 |false |radius_fb| + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |14 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |12 |true (ByPropertyName)|SceneItemName| + +#### **Thickness** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |7 |false | + +#### **TiltXDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |2 |false |tilt_x_deg| + +#### **TiltYDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |3 |false |tilt_y_deg| + +#### **TiltZDeg** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |4 |false |tilt_z_deg| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Wiggle** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |11 |false | + +#### **WiggleRot** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |wiggle_rot| + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBS3dPanelShader; CommonParameters=True; parameter=System.Object[]}} +``` From e819f983682fa39968e5983bc8c6651b9a73e014 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:03 +0000 Subject: [PATCH 048/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Set-OBS3dPanelShader.json | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBS3dPanelShader.json diff --git a/docs/_data/Help/Set-OBS3dPanelShader.json b/docs/_data/Help/Set-OBS3dPanelShader.json new file mode 100644 index 00000000..fe53f972 --- /dev/null +++ b/docs/_data/Help/Set-OBS3dPanelShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From fca6902cb96c0f301920ce1c45abae01e26d3435 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:05 +0000 Subject: [PATCH 049/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSCubeRotatingShader.md | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Set-OBSCubeRotatingShader.md diff --git a/docs/Set-OBSCubeRotatingShader.md b/docs/Set-OBSCubeRotatingShader.md new file mode 100644 index 00000000..5c393585 --- /dev/null +++ b/docs/Set-OBSCubeRotatingShader.md @@ -0,0 +1,114 @@ +Get-OBSCubeRotatingShader +------------------------- + +### Synopsis + +Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |7 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Images** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |0 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OtherImage1** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |3 |false |other_image1| + +#### **OtherImage2** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |other_image2| + +#### **OtherImage3** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |5 |false |other_image3| + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |8 |false |ShaderContent| + +#### **Shadow** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |6 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSCubeRotatingShader; CommonParameters=True; parameter=System.Object[]}} +``` From 883d19334656b8c8fc5780683389cf2d423d82a2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:05 +0000 Subject: [PATCH 050/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../_data/Help/Set-OBSCubeRotatingShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSCubeRotatingShader.json diff --git a/docs/_data/Help/Set-OBSCubeRotatingShader.json b/docs/_data/Help/Set-OBSCubeRotatingShader.json new file mode 100644 index 00000000..8d837b62 --- /dev/null +++ b/docs/_data/Help/Set-OBSCubeRotatingShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From f5b72767f4a3648fda584d6cb1b417ed55ab790c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:09 +0000 Subject: [PATCH 051/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSHardBlinkShader.md | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Set-OBSHardBlinkShader.md diff --git a/docs/Set-OBSHardBlinkShader.md b/docs/Set-OBSHardBlinkShader.md new file mode 100644 index 00000000..9be98bd9 --- /dev/null +++ b/docs/Set-OBSHardBlinkShader.md @@ -0,0 +1,90 @@ +Get-OBSHardBlinkShader +---------------------- + +### Synopsis + +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Timeoff** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **Timeon** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +``` From ce9cc0fe475d4336de94e64b44624641eb9ca547 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:09 +0000 Subject: [PATCH 052/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Set-OBSHardBlinkShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSHardBlinkShader.json diff --git a/docs/_data/Help/Set-OBSHardBlinkShader.json b/docs/_data/Help/Set-OBSHardBlinkShader.json new file mode 100644 index 00000000..43665759 --- /dev/null +++ b/docs/_data/Help/Set-OBSHardBlinkShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 3b6c24cc07d51d151a34ebd107825a2b7e6f4bfa Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:10 +0000 Subject: [PATCH 053/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSMotionBlurShader.md | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/Set-OBSMotionBlurShader.md diff --git a/docs/Set-OBSMotionBlurShader.md b/docs/Set-OBSMotionBlurShader.md new file mode 100644 index 00000000..6788858e --- /dev/null +++ b/docs/Set-OBSMotionBlurShader.md @@ -0,0 +1,90 @@ +Get-OBSMotionBlurShader +----------------------- + +### Synopsis + +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PreviousOutput** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------| +|`[string]`|false |0 |false |previous_output| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +``` From 27eecfb92f71bcb4f8cbcbca6c491cf8338fa229 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:10 +0000 Subject: [PATCH 054/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Set-OBSMotionBlurShader.json | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSMotionBlurShader.json diff --git a/docs/_data/Help/Set-OBSMotionBlurShader.json b/docs/_data/Help/Set-OBSMotionBlurShader.json new file mode 100644 index 00000000..eb301da6 --- /dev/null +++ b/docs/_data/Help/Set-OBSMotionBlurShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 49cd01c9c95f8643a97b85766b48a2a5dfdf08de Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:11 +0000 Subject: [PATCH 055/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSPerspectiveShader.md | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Set-OBSPerspectiveShader.md diff --git a/docs/Set-OBSPerspectiveShader.md b/docs/Set-OBSPerspectiveShader.md new file mode 100644 index 00000000..b39795f2 --- /dev/null +++ b/docs/Set-OBSPerspectiveShader.md @@ -0,0 +1,114 @@ +Get-OBSPerspectiveShader +------------------------ + +### Synopsis + +Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AngleX** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |0 |false |angle_x| + +#### **AngleY** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |1 |false |angle_y| + +#### **AngleZ** + +|Type |Required|Position|PipelineInput|Aliases| +|---------|--------|--------|-------------|-------| +|`[float]`|false |2 |false |angle_z| + +#### **BorderColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[string]`|false |4 |false |border_color| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Perspective** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |show_border| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSPerspectiveShader; CommonParameters=True; parameter=System.Object[]}} +``` From effb037a545a4c645010f988972f4c1a631f28ba Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:11 +0000 Subject: [PATCH 056/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/_data/Help/Set-OBSPerspectiveShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSPerspectiveShader.json diff --git a/docs/_data/Help/Set-OBSPerspectiveShader.json b/docs/_data/Help/Set-OBSPerspectiveShader.json new file mode 100644 index 00000000..43953a39 --- /dev/null +++ b/docs/_data/Help/Set-OBSPerspectiveShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From f387ee30c52816bfeb5a49c2e085cd4890222f5f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:12 +0000 Subject: [PATCH 057/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSRepeatGridCenterCropShader.md | 108 ++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Set-OBSRepeatGridCenterCropShader.md diff --git a/docs/Set-OBSRepeatGridCenterCropShader.md b/docs/Set-OBSRepeatGridCenterCropShader.md new file mode 100644 index 00000000..bd68baeb --- /dev/null +++ b/docs/Set-OBSRepeatGridCenterCropShader.md @@ -0,0 +1,108 @@ +Get-OBSRepeatGridCenterCropShader +--------------------------------- + +### Synopsis + +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Alpha** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **Columns** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |6 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Image** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[string]`|false |1 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Rows** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |4 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |7 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ViewProj** + +|Type |Required|Position|PipelineInput| +|-------------|--------|--------|-------------| +|`[float[][]]`|false |0 |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From 9273cc7b3a726982b3f7a0ea6f014dd4299172b6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:12 +0000 Subject: [PATCH 058/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Set-OBSRepeatGridCenterCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json diff --git a/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json new file mode 100644 index 00000000..c4ac45ff --- /dev/null +++ b/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 3a94309648df02d95d4aeb12c5f2d6ec904f2ebe Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:15 +0000 Subject: [PATCH 059/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/Set-OBSWalkingDeadPixelFixerShader.md | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/Set-OBSWalkingDeadPixelFixerShader.md diff --git a/docs/Set-OBSWalkingDeadPixelFixerShader.md b/docs/Set-OBSWalkingDeadPixelFixerShader.md new file mode 100644 index 00000000..fb0a4df0 --- /dev/null +++ b/docs/Set-OBSWalkingDeadPixelFixerShader.md @@ -0,0 +1,138 @@ +Get-OBSWalkingDeadPixelFixerShader +---------------------------------- + +### Synopsis + +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **Bypass** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ContrastThreshold** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------------| +|`[float]`|false |4 |false |Contrast_Threshold| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |8 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaxClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |6 |false |Max_Cluster_Size| + +#### **MinClusterSize** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------------| +|`[int]`|false |5 |false |Min_Cluster_Size| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ScanHeight** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-----------| +|`[int]`|false |1 |false |Scan_Height| + +#### **ScanOffsetX** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |2 |false |Scan_Offset_X| + +#### **ScanOffsetY** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|-------------| +|`[int]`|false |3 |false |Scan_Offset_Y| + +#### **ScanWidth** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|----------| +|`[int]`|false |0 |false |Scan_Width| + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |9 |false |ShaderContent| + +#### **ShowBorder** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |Show_Border| + +#### **ShowGreen** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[switch]`|false |Named |false |Show_Green| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +``` From a805553d6b91d3bf003d6f0b608d28717f17733b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:15 +0000 Subject: [PATCH 060/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- .../Set-OBSWalkingDeadPixelFixerShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json diff --git a/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json new file mode 100644 index 00000000..858f3e99 --- /dev/null +++ b/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 07beecc1527bf5f786e0d852cd1c2b38deebd57e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:44 +0000 Subject: [PATCH 061/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- docs/obs-powershell-commands.md | 354 ++++++++++++++++---------------- 1 file changed, 177 insertions(+), 177 deletions(-) diff --git a/docs/obs-powershell-commands.md b/docs/obs-powershell-commands.md index 552443d8..131c7af3 100644 --- a/docs/obs-powershell-commands.md +++ b/docs/obs-powershell-commands.md @@ -12,19 +12,19 @@ Functions ========= -|Name |Synopsis| -|------------------------------------------------------------------------------------------|--------| -|[Add-OBSInput](Add-OBSInput.md) | -|[Add-OBSProfile](Add-OBSProfile.md) | -|[Add-OBSScene](Add-OBSScene.md) | -|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) | -|[Add-OBSSceneItem](Add-OBSSceneItem.md) | -|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) | -|[Clear-OBSScene](Clear-OBSScene.md) | -|[Connect-OBS](Connect-OBS.md) | -|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) | -|[Disconnect-OBS](Disconnect-OBS.md) | -|[Get-OBS](Get-OBS.md) | +|Name |Synopsis | +|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| +|[Add-OBSInput](Add-OBSInput.md) |Add-OBSInput : CreateInput | +|[Add-OBSProfile](Add-OBSProfile.md) |Add-OBSProfile : CreateProfile | +|[Add-OBSScene](Add-OBSScene.md) |Add-OBSScene : CreateScene | +|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) |Add-OBSSceneCollection : CreateSceneCollection | +|[Add-OBSSceneItem](Add-OBSSceneItem.md) |Add-OBSSceneItem : CreateSceneItem | +|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) |Add-OBSSourceFilter : CreateSourceFilter | +|[Clear-OBSScene](Clear-OBSScene.md) |Clears a Scene in OBS | +|[Connect-OBS](Connect-OBS.md) |Connects to Open Broadcast Studio | +|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | +|[Disconnect-OBS](Disconnect-OBS.md) |Disconnects OBS | +|[Get-OBS](Get-OBS.md) |Gets OBS | |[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | @@ -53,10 +53,10 @@ Functions |[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) | -|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) | -|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) | -|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | +|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | +|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | +|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) |Get-OBSCurrentSceneTransitionCursor : GetCurrentSceneTransitionCursor | |[Get-OBSCurveShader](Get-OBSCurveShader.md) | |[Get-OBSCutRectPerCornerShader](Get-OBSCutRectPerCornerShader.md) | |[Get-OBSCylinderShader](Get-OBSCylinderShader.md) | @@ -72,7 +72,7 @@ Functions |[Get-OBSDrunkShader](Get-OBSDrunkShader.md) | |[Get-OBSDynamicMaskShader](Get-OBSDynamicMaskShader.md) | |[Get-OBSEdgeDetectionShader](Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](Get-OBSEffect.md) | +|[Get-OBSEffect](Get-OBSEffect.md) |Gets OBS Effects | |[Get-OBSEmbersShader](Get-OBSEmbersShader.md) | |[Get-OBSEmbossColorShader](Get-OBSEmbossColorShader.md) | |[Get-OBSEmbossShader](Get-OBSEmbossShader.md) | @@ -103,61 +103,61 @@ Functions |[Get-OBSGlitchShader](Get-OBSGlitchShader.md) | |[Get-OBSGlowShader](Get-OBSGlowShader.md) | |[Get-OBSGradientShader](Get-OBSGradientShader.md) | -|[Get-OBSGroup](Get-OBSGroup.md) | -|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) | +|[Get-OBSGroup](Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | +|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | |[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | |[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](Get-OBSHotkey.md) | +|[Get-OBSHotkey](Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | |[Get-OBSHslHsvSaturationShader](Get-OBSHslHsvSaturationShader.md) | |[Get-OBSHueRotatonShader](Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](Get-OBSInput.md) | -|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) | -|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) | -|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) | -|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) | -|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) | -|[Get-OBSInputKind](Get-OBSInputKind.md) | -|[Get-OBSInputMute](Get-OBSInputMute.md) | -|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md)| -|[Get-OBSInputSettings](Get-OBSInputSettings.md) | -|[Get-OBSInputVolume](Get-OBSInputVolume.md) | +|[Get-OBSInput](Get-OBSInput.md) |Get-OBSInput : GetInputList | +|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) |Get-OBSInputAudioBalance : GetInputAudioBalance | +|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) |Get-OBSInputAudioMonitorType : GetInputAudioMonitorType | +|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) |Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset | +|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) |Get-OBSInputAudioTracks : GetInputAudioTracks | +|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) |Get-OBSInputDefaultSettings : GetInputDefaultSettings | +|[Get-OBSInputKind](Get-OBSInputKind.md) |Get-OBSInputKind : GetInputKindList | +|[Get-OBSInputMute](Get-OBSInputMute.md) |Get-OBSInputMute : GetInputMute | +|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md)|Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems| +|[Get-OBSInputSettings](Get-OBSInputSettings.md) |Get-OBSInputSettings : GetInputSettings | +|[Get-OBSInputVolume](Get-OBSInputVolume.md) |Get-OBSInputVolume : GetInputVolume | |[Get-OBSIntensityScopeShader](Get-OBSIntensityScopeShader.md) | |[Get-OBSInvertLumaShader](Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) |Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay | |[Get-OBSLuminance2Shader](Get-OBSLuminance2Shader.md) | |[Get-OBSLuminanceAlphaShader](Get-OBSLuminanceAlphaShader.md) | |[Get-OBSLuminanceShader](Get-OBSLuminanceShader.md) | |[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) | -|[Get-OBSMonitor](Get-OBSMonitor.md) | +|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | +|[Get-OBSMonitor](Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | |[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | -|[Get-OBSOutput](Get-OBSOutput.md) | -|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) | -|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) | +|[Get-OBSOutput](Get-OBSOutput.md) |Get-OBSOutput : GetOutputList | +|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) |Get-OBSOutputSettings : GetOutputSettings | +|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) |Get-OBSOutputStatus : GetOutputStatus | |[Get-OBSPagePeelShader](Get-OBSPagePeelShader.md) | |[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](Get-OBSPersistentData.md) | +|[Get-OBSPersistentData](Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | |[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | |[Get-OBSPolarShader](Get-OBSPolarShader.md) | -|[Get-OBSProfile](Get-OBSProfile.md) | -|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) | +|[Get-OBSProfile](Get-OBSProfile.md) |Get-OBSProfile : GetProfileList | +|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) |Get-OBSProfileParameter : GetProfileParameter | |[Get-OBSPulseShader](Get-OBSPulseShader.md) | |[Get-OBSRainbowShader](Get-OBSRainbowShader.md) | |[Get-OBSRainWindowShader](Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) | -|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) | +|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) |Get-OBSRecordDirectory : GetRecordDirectory | +|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) |Get-OBSRecordStatus : GetRecordStatus | |[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | |[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) | +|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | |[Get-OBSRGBAPercentShader](Get-OBSRGBAPercentShader.md) | |[Get-OBSRgbColorWheelShader](Get-OBSRgbColorWheelShader.md) | |[Get-OBSRgbSplitShader](Get-OBSRgbSplitShader.md) | @@ -173,18 +173,18 @@ Functions |[Get-OBSRoundedStrokeGradientShader](Get-OBSRoundedStrokeGradientShader.md) | |[Get-OBSRoundedStrokeShader](Get-OBSRoundedStrokeShader.md) | |[Get-OBSScanLineShader](Get-OBSScanLineShader.md) | -|[Get-OBSScene](Get-OBSScene.md) | -|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) | -|[Get-OBSSceneItem](Get-OBSSceneItem.md) | -|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) | -|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) | -|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) | -|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) | -|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) | -|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) | -|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) | -|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) | -|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) | +|[Get-OBSScene](Get-OBSScene.md) |Get-OBSScene : GetSceneList | +|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) |Get-OBSSceneCollection : GetSceneCollectionList | +|[Get-OBSSceneItem](Get-OBSSceneItem.md) |Get-OBSSceneItem : GetSceneItemList | +|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) |Get-OBSSceneItemBlendMode : GetSceneItemBlendMode | +|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) |Get-OBSSceneItemEnabled : GetSceneItemEnabled | +|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) |Get-OBSSceneItemId : GetSceneItemId | +|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) |Get-OBSSceneItemIndex : GetSceneItemIndex | +|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) |Get-OBSSceneItemLocked : GetSceneItemLocked | +|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) |Get-OBSSceneItemSource : GetSceneItemSource | +|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) |Get-OBSSceneItemTransform : GetSceneItemTransform | +|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) |Get-OBSSceneSceneTransitionOverride : GetSceneSceneTransitionOverride | +|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) |Get-OBSSceneTransition : GetSceneTransitionList | |[Get-OBSSeascapeShader](Get-OBSSeascapeShader.md) | |[Get-OBSSeasickShader](Get-OBSSeasickShader.md) | |[Get-OBSSelectiveColorShader](Get-OBSSelectiveColorShader.md) | @@ -193,143 +193,143 @@ Functions |[Get-OBSSimpleGradientShader](Get-OBSSimpleGradientShader.md) | |[Get-OBSSimplexNoiseShader](Get-OBSSimplexNoiseShader.md) | |[Get-OBSSmartDenoiseShader](Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](Get-OBSSourceActive.md) | -|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) | -|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) | -|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) | -|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) | -|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) | -|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) | +|[Get-OBSSourceActive](Get-OBSSourceActive.md) |Get-OBSSourceActive : GetSourceActive | +|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) |Get-OBSSourceFilter : GetSourceFilter | +|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) |Get-OBSSourceFilterDefaultSettings : GetSourceFilterDefaultSettings | +|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) |Get-OBSSourceFilterKind : GetSourceFilterKindList | +|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) |Get-OBSSourceFilterList : GetSourceFilterList | +|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) |Get-OBSSourceScreenshot : GetSourceScreenshot | +|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) |Get-OBSSpecialInputs : GetSpecialInputs | |[Get-OBSSpecularShineShader](Get-OBSSpecularShineShader.md) | |[Get-OBSSpotlightShader](Get-OBSSpotlightShader.md) | -|[Get-OBSStats](Get-OBSStats.md) | -|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) | -|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) | -|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) | +|[Get-OBSStats](Get-OBSStats.md) |Get-OBSStats : GetStats | +|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) |Get-OBSStreamServiceSettings : GetStreamServiceSettings | +|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) |Get-OBSStreamStatus : GetStreamStatus | +|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) |Get-OBSStudioModeEnabled : GetStudioModeEnabled | |[Get-OBSSwirlShader](Get-OBSSwirlShader.md) | |[Get-OBSTetraShader](Get-OBSTetraShader.md) | |[Get-OBSThermalShader](Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) | +|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) |Get-OBSTransitionKind : GetTransitionKindList | |[Get-OBSTvCrtSubpixelShader](Get-OBSTvCrtSubpixelShader.md) | |[Get-OBSTwistShader](Get-OBSTwistShader.md) | |[Get-OBSTwoPassDropShadowShader](Get-OBSTwoPassDropShadowShader.md) | |[Get-OBSVCRShader](Get-OBSVCRShader.md) | -|[Get-OBSVersion](Get-OBSVersion.md) | +|[Get-OBSVersion](Get-OBSVersion.md) |Get-OBSVersion : GetVersion | |[Get-OBSVHSShader](Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) | +|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) |Get-OBSVideoSettings : GetVideoSettings | |[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) | +|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | |[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | |[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](Get-OBSZoomShader.md) | |[Get-OBSZoomXYShader](Get-OBSZoomXYShader.md) | -|[Hide-OBS](Hide-OBS.md) | -|[Import-OBSEffect](Import-OBSEffect.md) | -|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) | -|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) | -|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) | -|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) | -|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) | -|[Receive-OBS](Receive-OBS.md) | -|[Remove-OBS](Remove-OBS.md) | -|[Remove-OBSEffect](Remove-OBSEffect.md) | -|[Remove-OBSInput](Remove-OBSInput.md) | -|[Remove-OBSProfile](Remove-OBSProfile.md) | -|[Remove-OBSScene](Remove-OBSScene.md) | -|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) | -|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) | -|[Resume-OBSRecord](Resume-OBSRecord.md) | -|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) | -|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) | -|[Send-OBS](Send-OBS.md) | -|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) | -|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) | -|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) | -|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) | -|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) | -|[Send-OBSSleep](Send-OBSSleep.md) | -|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) | -|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) | -|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) | -|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) | -|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) | -|[Set-OBS3DFilter](Set-OBS3DFilter.md) | -|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) | -|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) | -|[Set-OBSColorFilter](Set-OBSColorFilter.md) | -|[Set-OBSColorSource](Set-OBSColorSource.md) | -|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) | -|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) | -|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) | -|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) | -|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) | -|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) | -|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) | -|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) | -|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) | -|[Set-OBSGainFilter](Set-OBSGainFilter.md) | -|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) | -|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) | -|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) | -|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) | -|[Set-OBSInputMute](Set-OBSInputMute.md) | -|[Set-OBSInputName](Set-OBSInputName.md) | -|[Set-OBSInputSettings](Set-OBSInputSettings.md) | -|[Set-OBSInputVolume](Set-OBSInputVolume.md) | -|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) | -|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) | -|[Set-OBSMediaSource](Set-OBSMediaSource.md) | -|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) | -|[Set-OBSPersistentData](Set-OBSPersistentData.md) | -|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) | -|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) | -|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) | -|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) | -|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) | -|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) | -|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) | -|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) | -|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) | -|[Set-OBSSceneName](Set-OBSSceneName.md) | -|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) | -|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) | -|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) | -|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) | -|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) | -|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) | -|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) | -|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) | -|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) | -|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) | -|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) | -|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) | -|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) | -|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) | -|[Set-OBSVLCSource](Set-OBSVLCSource.md) | -|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | -|[Set-OBSWindowSource](Set-OBSWindowSource.md) | -|[Show-OBS](Show-OBS.md) | -|[Start-OBSEffect](Start-OBSEffect.md) | -|[Start-OBSOutput](Start-OBSOutput.md) | -|[Start-OBSRecord](Start-OBSRecord.md) | -|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | -|[Start-OBSStream](Start-OBSStream.md) | -|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | -|[Stop-OBSEffect](Stop-OBSEffect.md) | -|[Stop-OBSOutput](Stop-OBSOutput.md) | -|[Stop-OBSRecord](Stop-OBSRecord.md) | -|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) | -|[Stop-OBSStream](Stop-OBSStream.md) | -|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) | -|[Switch-OBSInputMute](Switch-OBSInputMute.md) | -|[Switch-OBSOutput](Switch-OBSOutput.md) | -|[Switch-OBSRecord](Switch-OBSRecord.md) | -|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) | -|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) | -|[Switch-OBSStream](Switch-OBSStream.md) | -|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) | -|[Watch-OBS](Watch-OBS.md) | +|[Hide-OBS](Hide-OBS.md) |Hide OBS | +|[Import-OBSEffect](Import-OBSEffect.md) |Imports Effects | +|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) |Open-OBSInputFiltersDialog : OpenInputFiltersDialog | +|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) |Open-OBSInputInteractDialog : OpenInputInteractDialog | +|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) |Open-OBSInputPropertiesDialog : OpenInputPropertiesDialog | +|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) |Open-OBSSourceProjector : OpenSourceProjector | +|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) |Open-OBSVideoMixProjector : OpenVideoMixProjector | +|[Receive-OBS](Receive-OBS.md) |Receives data from OBS | +|[Remove-OBS](Remove-OBS.md) |Remove OBS | +|[Remove-OBSEffect](Remove-OBSEffect.md) |Removes OBS Effects | +|[Remove-OBSInput](Remove-OBSInput.md) |Remove-OBSInput : RemoveInput | +|[Remove-OBSProfile](Remove-OBSProfile.md) |Remove-OBSProfile : RemoveProfile | +|[Remove-OBSScene](Remove-OBSScene.md) |Remove-OBSScene : RemoveScene | +|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) |Remove-OBSSceneItem : RemoveSceneItem | +|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) |Remove-OBSSourceFilter : RemoveSourceFilter | +|[Resume-OBSRecord](Resume-OBSRecord.md) |Resume-OBSRecord : ResumeRecord | +|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) |Save-OBSReplayBuffer : SaveReplayBuffer | +|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) |Save-OBSSourceScreenshot : SaveSourceScreenshot | +|[Send-OBS](Send-OBS.md) |Sends messages to the OBS websocket. | +|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) |Send-OBSCallVendorRequest : CallVendorRequest | +|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) |Send-OBSCustomEvent : BroadcastCustomEvent | +|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) |Send-OBSOffsetMediaInputCursor : OffsetMediaInputCursor | +|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) |Send-OBSPauseRecord : PauseRecord | +|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) |Send-OBSPressInputPropertiesButton : PressInputPropertiesButton | +|[Send-OBSSleep](Send-OBSSleep.md) |Send-OBSSleep : Sleep | +|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) |Send-OBSStreamCaption : SendStreamCaption | +|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) |Send-OBSTriggerHotkeyByKeySequence : TriggerHotkeyByKeySequence | +|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) |Send-OBSTriggerHotkeyByName : TriggerHotkeyByName | +|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) |Send-OBSTriggerMediaInputAction : TriggerMediaInputAction | +|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) |Send-OBSTriggerStudioModeTransition : TriggerStudioModeTransition | +|[Set-OBS3DFilter](Set-OBS3DFilter.md) |Sets an OBS 3D Filter. | +|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) |Adds or sets an audio output source | +|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) |Sets a browser source | +|[Set-OBSColorFilter](Set-OBSColorFilter.md) |Sets a color filter | +|[Set-OBSColorSource](Set-OBSColorSource.md) |Adds a color source | +|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) |Set-OBSCurrentPreviewScene : SetCurrentPreviewScene | +|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) |Set-OBSCurrentProfile : SetCurrentProfile | +|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) |Set-OBSCurrentProgramScene : SetCurrentProgramScene | +|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) |Set-OBSCurrentSceneCollection : SetCurrentSceneCollection | +|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) |Set-OBSCurrentSceneTransition : SetCurrentSceneTransition | +|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) |Set-OBSCurrentSceneTransitionDuration : SetCurrentSceneTransitionDuration | +|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) |Set-OBSCurrentSceneTransitionSettings : SetCurrentSceneTransitionSettings | +|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) |Adds a display source | +|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) |Sets a Equalizer filter. | +|[Set-OBSGainFilter](Set-OBSGainFilter.md) |Sets a Gain filter. | +|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) |Set-OBSInputAudioBalance : SetInputAudioBalance | +|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) |Set-OBSInputAudioMonitorType : SetInputAudioMonitorType | +|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) |Set-OBSInputAudioSyncOffset : SetInputAudioSyncOffset | +|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) |Set-OBSInputAudioTracks : SetInputAudioTracks | +|[Set-OBSInputMute](Set-OBSInputMute.md) |Set-OBSInputMute : SetInputMute | +|[Set-OBSInputName](Set-OBSInputName.md) |Set-OBSInputName : SetInputName | +|[Set-OBSInputSettings](Set-OBSInputSettings.md) |Set-OBSInputSettings : SetInputSettings | +|[Set-OBSInputVolume](Set-OBSInputVolume.md) |Set-OBSInputVolume : SetInputVolume | +|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) |Sets a markdown source | +|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) |Set-OBSMediaInputCursor : SetMediaInputCursor | +|[Set-OBSMediaSource](Set-OBSMediaSource.md) |Adds a media source | +|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) |Set-OBSOutputSettings : SetOutputSettings | +|[Set-OBSPersistentData](Set-OBSPersistentData.md) |Set-OBSPersistentData : SetPersistentData | +|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) |Set-OBSProfileParameter : SetProfileParameter | +|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) |Set-OBSRecordDirectory : SetRecordDirectory | +|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) |Sets a RenderDelay filter. | +|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) |Sets a Scale filter. | +|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) |Set-OBSSceneItemBlendMode : SetSceneItemBlendMode | +|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) |Set-OBSSceneItemEnabled : SetSceneItemEnabled | +|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) |Set-OBSSceneItemIndex : SetSceneItemIndex | +|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) |Set-OBSSceneItemLocked : SetSceneItemLocked | +|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) |Set-OBSSceneItemTransform : SetSceneItemTransform | +|[Set-OBSSceneName](Set-OBSSceneName.md) |Set-OBSSceneName : SetSceneName | +|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) |Set-OBSSceneSceneTransitionOverride : SetSceneSceneTransitionOverride | +|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) |Sets a scroll filter. | +|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) |Sets a Shader filter. | +|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) |Sets a Sharpness filter. | +|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) |Sets a Sound Cloud Source | +|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) |Set-OBSSourceFilterEnabled : SetSourceFilterEnabled | +|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) |Set-OBSSourceFilterIndex : SetSourceFilterIndex | +|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) |Set-OBSSourceFilterName : SetSourceFilterName | +|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) |Set-OBSSourceFilterSettings : SetSourceFilterSettings | +|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) |Set-OBSStreamServiceSettings : SetStreamServiceSettings | +|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) |Set-OBSStudioModeEnabled : SetStudioModeEnabled | +|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) |Adds a VLC playlist source | +|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) |Set-OBSTBarPosition : SetTBarPosition | +|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) |Set-OBSVideoSettings : SetVideoSettings | +|[Set-OBSVLCSource](Set-OBSVLCSource.md) |Adds a VLC playlist source | +|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) |OBS Waveform Source | +|[Set-OBSWindowSource](Set-OBSWindowSource.md) |Adds or sets a window capture source | +|[Show-OBS](Show-OBS.md) |Shows content in OBS | +|[Start-OBSEffect](Start-OBSEffect.md) |Starts obs-powershell effects. | +|[Start-OBSOutput](Start-OBSOutput.md) |Start-OBSOutput : StartOutput | +|[Start-OBSRecord](Start-OBSRecord.md) |Start-OBSRecord : StartRecord | +|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) |Start-OBSReplayBuffer : StartReplayBuffer | +|[Start-OBSStream](Start-OBSStream.md) |Start-OBSStream : StartStream | +|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) |Start-OBSVirtualCam : StartVirtualCam | +|[Stop-OBSEffect](Stop-OBSEffect.md) |Stops obs-powershell effects. | +|[Stop-OBSOutput](Stop-OBSOutput.md) |Stop-OBSOutput : StopOutput | +|[Stop-OBSRecord](Stop-OBSRecord.md) |Stop-OBSRecord : StopRecord | +|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) |Stop-OBSReplayBuffer : StopReplayBuffer | +|[Stop-OBSStream](Stop-OBSStream.md) |Stop-OBSStream : StopStream | +|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) |Stop-OBSVirtualCam : StopVirtualCam | +|[Switch-OBSInputMute](Switch-OBSInputMute.md) |Switch-OBSInputMute : ToggleInputMute | +|[Switch-OBSOutput](Switch-OBSOutput.md) |Switch-OBSOutput : ToggleOutput | +|[Switch-OBSRecord](Switch-OBSRecord.md) |Switch-OBSRecord : ToggleRecord | +|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) |Switch-OBSRecordPause : ToggleRecordPause | +|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) |Switch-OBSReplayBuffer : ToggleReplayBuffer | +|[Switch-OBSStream](Switch-OBSStream.md) |Switch-OBSStream : ToggleStream | +|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) |Switch-OBSVirtualCam : ToggleVirtualCam | +|[Watch-OBS](Watch-OBS.md) |Watches OBS | From 3fd8232dbba136ff61f77150005e6237e2af3b51 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:54 +0000 Subject: [PATCH 062/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- obs-powershell-Help.xml | 5074 +++++++++++++++++++++++++++++---------- 1 file changed, 3793 insertions(+), 1281 deletions(-) diff --git a/obs-powershell-Help.xml b/obs-powershell-Help.xml index 478fc2fa..300200e1 100644 --- a/obs-powershell-Help.xml +++ b/obs-powershell-Help.xml @@ -1437,6 +1437,526 @@ This can increase performance, and also silently ignore critical errors + + + Get-OBS3dPanelShader + OBS3dPanelShader + Get + + +Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-TiltXDeg] <float>] [[-TiltYDeg] <float>] [[-TiltZDeg] <float>] [[-PosX] <float>] [[-PosY] <float>] [[-Thickness] <float>] [[-RadiusFb] <float>] [[-Brightness] <float>] [[-LightPosition] <int>] [[-Wiggle] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBS3dPanelShader + + Credits + + + String + + String + + + + + + + Scale + + + Float + + Float + + + + + + + TiltXDeg + + + Float + + Float + + + + + + + TiltYDeg + + + Float + + Float + + + + + + + TiltZDeg + + + Float + + Float + + + + + + + PosX + + + Float + + Float + + + + + + + PosY + + + Float + + Float + + + + + + + Thickness + + + Float + + Float + + + + + + + RadiusFb + + + Float + + Float + + + + + + + Brightness + + + Float + + Float + + + + + + + LightPosition + + + Int + + Int + + + + + + + Wiggle + + + Float + + Float + + + + + + + WiggleRot + + + Switch + + Switch + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + Brightness + + + Float + + Float + + + + + + + Credits + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + LightPosition + + + Int + + Int + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + PosX + + + Float + + Float + + + + + + + PosY + + + Float + + Float + + + + + + + RadiusFb + + + Float + + Float + + + + + + + Scale + + + Float + + Float + + + + + + + ShaderText + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + + + Thickness + + + Float + + Float + + + + + + + TiltXDeg + + + Float + + Float + + + + + + + TiltYDeg + + + Float + + Float + + + + + + + TiltZDeg + + + Float + + Float + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + Wiggle + + + Float + + Float + + + + + + + WiggleRot + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBS3dSwapTransitionShader @@ -13785,6 +14305,358 @@ This can increase performance, and also silently ignore critical errors + + + Get-OBSCubeRotatingShader + OBSCubeRotatingShader + Get + + +Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Shadow] <float>] [[-OtherImage1] <string>] [[-OtherImage2] <string>] [[-OtherImage3] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBSCubeRotatingShader + + Images + + + Int + + Int + + + + + + + Speed + + + Float + + Float + + + + + + + Shadow + + + Float + + Float + + + + + + + OtherImage1 + + + String + + String + + + + + + + OtherImage2 + + + String + + String + + + + + + + OtherImage3 + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + Images + + + Int + + Int + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + OtherImage1 + + + String + + String + + + + + + + OtherImage2 + + + String + + String + + + + + + + OtherImage3 + + + String + + String + + + + + + + PassThru + + + Switch + + Switch + + + + + + + ShaderText + + + String + + String + + + + + + + Shadow + + + Float + + Float + + + + + + + SourceName + + + String + + String + + + + + + + Speed + + + Float + + Float + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBSCurrentPreviewScene @@ -35866,6 +36738,262 @@ This can increase performance, and also silently ignore critical errors + + + Get-OBSHardBlinkShader + OBSHardBlinkShader + Get + + +Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBSHardBlinkShader + + Timeon + + + Float + + Float + + + + + + + Timeoff + + + Float + + Float + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + ShaderText + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + + + Timeoff + + + Float + + Float + + + + + + + Timeon + + + Float + + Float + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBSHeatWaveSimpleShader @@ -42231,11 +43359,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSMultiplyShader - OBSMultiplyShader + Get-OBSMotionBlurShader + OBSMotionBlurShader Get - Get-OBSMultiplyShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -42243,12 +43373,10 @@ This can increase performance, and also silently ignore critical errors - Get-OBSMultiplyShader + Get-OBSMotionBlurShader - OtherImage + PreviousOutput - - String @@ -42258,11 +43386,21 @@ This can increase performance, and also silently ignore critical errors - + + Strength + + + Float + + Float + + + + + + SourceName - - String @@ -42272,11 +43410,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -42286,11 +43422,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -42303,8 +43437,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -42317,8 +43449,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -42331,8 +43461,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -42345,8 +43473,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -42362,8 +43488,6 @@ This can increase performance, and also silently ignore critical errors FilterName - - String @@ -42376,8 +43500,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -42390,8 +43512,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -42402,28 +43522,24 @@ This can increase performance, and also silently ignore critical errors - OtherImage + PassThru - - - String + Switch - String + Switch - PassThru + PreviousOutput - - - Switch + String - Switch + String @@ -42432,8 +43548,6 @@ This can increase performance, and also silently ignore critical errors ShaderText - - String @@ -42446,8 +43560,6 @@ This can increase performance, and also silently ignore critical errors SourceName - - String @@ -42457,11 +43569,283 @@ This can increase performance, and also silently ignore critical errors + + Strength + + + Float + + Float + + + + + UseShaderTime - - + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSMultiplyShader + OBSMultiplyShader + Get + + Get-OBSMultiplyShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSMultiplyShader + + OtherImage + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + OtherImage + + + + + String + + String + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + Switch @@ -45172,6 +46556,358 @@ This can increase performance, and also silently ignore critical errors + + + Get-OBSPerspectiveShader + OBSPerspectiveShader + Get + + +Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[-AngleZ] <float>] [[-Perspective] <float>] [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBSPerspectiveShader + + AngleX + + + Float + + Float + + + + + + + AngleY + + + Float + + Float + + + + + + + AngleZ + + + Float + + Float + + + + + + + Perspective + + + Float + + Float + + + + + + + BorderColor + + + String + + String + + + + + + + ShowBorder + + + Switch + + Switch + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + AngleX + + + Float + + Float + + + + + + + AngleY + + + Float + + Float + + + + + + + AngleZ + + + Float + + Float + + + + + + + BorderColor + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + Perspective + + + Float + + Float + + + + + + + ShaderText + + + String + + String + + + + + + + ShowBorder + + + Switch + + Switch + + + + + + + SourceName + + + String + + String + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBSPieChartShader @@ -50143,11 +51879,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSRepeatShader - OBSRepeatShader + Get-OBSRepeatGridCenterCropShader + OBSRepeatGridCenterCropShader Get - Get-OBSRepeatShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Alpha] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-Alpha] <float>] [[-Columns] <float>] [[-Rows] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -50155,12 +51893,10 @@ This can increase performance, and also silently ignore critical errors - Get-OBSRepeatShader + Get-OBSRepeatGridCenterCropShader ViewProj - - System.Single[][] @@ -50171,52 +51907,8 @@ This can increase performance, and also silently ignore critical errors - ColorMatrix - - - - - System.Single[][] - - System.Single[][] - - - - - - - ColorRangeMin - - - - - System.Single[] - - System.Single[] - - - - - - - ColorRangeMax - - - - - System.Single[] - - System.Single[] - - - - - - Image - - String @@ -50226,81 +51918,9 @@ This can increase performance, and also silently ignore critical errors - - ElapsedTime - - - - - Float - - Float - - - - - - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - RandF + + Alpha - - Float @@ -50310,11 +51930,9 @@ This can increase performance, and also silently ignore critical errors - - Alpha + + Columns - - Float @@ -50324,11 +51942,9 @@ This can increase performance, and also silently ignore critical errors - - Copies + + Rows - - Float @@ -50338,25 +51954,9 @@ This can increase performance, and also silently ignore critical errors - - Notes - - - - - String - - String - - - - - - + SourceName - - String @@ -50366,11 +51966,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -50380,11 +51978,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -50397,8 +51993,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -50411,8 +52005,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -50425,8 +52017,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -50439,8 +52029,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -50456,64 +52044,6 @@ This can increase performance, and also silently ignore critical errors Alpha - - - - Float - - Float - - - - - - - ColorMatrix - - - - - System.Single[][] - - System.Single[][] - - - - - - - ColorRangeMax - - - - - System.Single[] - - System.Single[] - - - - - - - ColorRangeMin - - - - - System.Single[] - - System.Single[] - - - - - - - Copies - - - Float @@ -50524,10 +52054,8 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + Columns - - Float @@ -50540,8 +52068,6 @@ This can increase performance, and also silently ignore critical errors FilterName - - String @@ -50554,8 +52080,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -50568,8 +52092,6 @@ This can increase performance, and also silently ignore critical errors Image - - String @@ -50582,8 +52104,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -50593,25 +52113,9 @@ This can increase performance, and also silently ignore critical errors - - Notes - - - - - String - - String - - - - - PassThru - - Switch @@ -50622,10 +52126,8 @@ This can increase performance, and also silently ignore critical errors - RandF + Rows - - Float @@ -50638,8 +52140,6 @@ This can increase performance, and also silently ignore critical errors ShaderText - - String @@ -50652,8 +52152,6 @@ This can increase performance, and also silently ignore critical errors SourceName - - String @@ -50666,8 +52164,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -50677,67 +52173,9 @@ This can increase performance, and also silently ignore critical errors - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - ViewProj - - System.Single[][] @@ -50769,11 +52207,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSRepeatTextureShader - OBSRepeatTextureShader + Get-OBSRepeatShader + OBSRepeatShader Get - Get-OBSRepeatTextureShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-TexImage] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Blend] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSRepeatShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Alpha] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -50781,7 +52219,7 @@ This can increase performance, and also silently ignore critical errors - Get-OBSRepeatTextureShader + Get-OBSRepeatShader ViewProj @@ -50853,20 +52291,6 @@ This can increase performance, and also silently ignore critical errors - TexImage - - - - - String - - String - - - - - - ElapsedTime @@ -50880,7 +52304,7 @@ This can increase performance, and also silently ignore critical errors - + UvOffset @@ -50894,7 +52318,7 @@ This can increase performance, and also silently ignore critical errors - + UvScale @@ -50908,7 +52332,7 @@ This can increase performance, and also silently ignore critical errors - + UvPixelInterval @@ -50922,7 +52346,7 @@ This can increase performance, and also silently ignore critical errors - + UvSize @@ -50936,7 +52360,7 @@ This can increase performance, and also silently ignore critical errors - + RandF @@ -50950,8 +52374,8 @@ This can increase performance, and also silently ignore critical errors - - Blend + + Alpha @@ -50964,7 +52388,7 @@ This can increase performance, and also silently ignore critical errors - + Copies @@ -50978,7 +52402,7 @@ This can increase performance, and also silently ignore critical errors - + Notes @@ -50992,21 +52416,7 @@ This can increase performance, and also silently ignore critical errors - - AlphaPercentage - - - - - Float - - Float - - - - - - + SourceName @@ -51020,7 +52430,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -51034,7 +52444,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -51108,21 +52518,675 @@ This can increase performance, and also silently ignore critical errors - AlphaPercentage - - - - - Float - - Float - - - - - - - Blend + Alpha + + + + + Float + + Float + + + + + + + ColorMatrix + + + + + System.Single[][] + + System.Single[][] + + + + + + + ColorRangeMax + + + + + System.Single[] + + System.Single[] + + + + + + + ColorRangeMin + + + + + System.Single[] + + System.Single[] + + + + + + + Copies + + + + + Float + + Float + + + + + + + ElapsedTime + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Image + + + + + String + + String + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + RandF + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSRepeatTextureShader + OBSRepeatTextureShader + Get + + Get-OBSRepeatTextureShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-TexImage] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Blend] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSRepeatTextureShader + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + ColorMatrix + + + + + System.Single[][] + + System.Single[][] + + + + + + + ColorRangeMin + + + + + System.Single[] + + System.Single[] + + + + + + + ColorRangeMax + + + + + System.Single[] + + System.Single[] + + + + + + + Image + + + + + String + + String + + + + + + + TexImage + + + + + String + + String + + + + + + + ElapsedTime + + + + + Float + + Float + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + RandF + + + + + Float + + Float + + + + + + + Blend + + + + + Float + + Float + + + + + + + Copies + + + + + Float + + Float + + + + + + + Notes + + + + + String + + String + + + + + + + AlphaPercentage + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + AlphaPercentage + + + + + Float + + Float + + + + + + + Blend @@ -67280,15 +69344,414 @@ This can increase performance, and also silently ignore critical errors - Float + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Strength + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSTransitionKind + OBSTransitionKind + Get + + Get-OBSTransitionKind : GetTransitionKindList + + 0.2.0.1 + + + Gets an array of all available transition kinds. + Similar to `GetInputKindList` + Get-OBSTransitionKind calls the OBS WebSocket with a request of type GetTransitionKindList. + + + + Get-OBSTransitionKind + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSTransitionKind + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#gettransitionkindlist + + + + + + Get-OBSTvCrtSubpixelShader + OBSTvCrtSubpixelShader + Get + + Get-OBSTvCrtSubpixelShader [[-ChannelWidth] <int>] [[-ChannelHeight] <int>] [[-HGap] <int>] [[-VGap] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSTvCrtSubpixelShader + + ChannelWidth + + + + + Int + + Int + + + + + + + ChannelHeight + + + + + Int + + Int + + + + + + + HGap + + + + + Int - Float + Int - + + VGap + + + + + Int + + Int + + + + + + SourceName @@ -67302,7 +69765,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -67316,7 +69779,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -67389,6 +69852,34 @@ This can increase performance, and also silently ignore critical errors + + ChannelHeight + + + + + Int + + Int + + + + + + + ChannelWidth + + + + + Int + + Int + + + + + FilterName @@ -67417,6 +69908,20 @@ This can increase performance, and also silently ignore critical errors + + HGap + + + + + Int + + Int + + + + + NoResponse @@ -67474,28 +69979,28 @@ This can increase performance, and also silently ignore critical errors - Strength + UseShaderTime - Float + Switch - Float + Switch - UseShaderTime + VGap - Switch + Int - Switch + Int @@ -67523,106 +70028,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTransitionKind - OBSTransitionKind - Get - - Get-OBSTransitionKind : GetTransitionKindList - - 0.2.0.1 - - - Gets an array of all available transition kinds. - Similar to `GetInputKindList` - Get-OBSTransitionKind calls the OBS WebSocket with a request of type GetTransitionKindList. - - - - Get-OBSTransitionKind - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSTransitionKind - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#gettransitionkindlist - - - - - - Get-OBSTvCrtSubpixelShader - OBSTvCrtSubpixelShader + Get-OBSTwistShader + OBSTwistShader Get - Get-OBSTvCrtSubpixelShader [[-ChannelWidth] <int>] [[-ChannelHeight] <int>] [[-HGap] <int>] [[-VGap] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSTwistShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-Power] <float>] [[-Rotation] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -67630,9 +70040,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTvCrtSubpixelShader + Get-OBSTwistShader - ChannelWidth + CenterXPercent @@ -67646,7 +70056,7 @@ This can increase performance, and also silently ignore critical errors - ChannelHeight + CenterYPercent @@ -67660,28 +70070,28 @@ This can increase performance, and also silently ignore critical errors - HGap + Power - Int + Float - Int + Float - VGap + Rotation - Int + Float - Int + Float @@ -67789,7 +70199,7 @@ This can increase performance, and also silently ignore critical errors - ChannelHeight + CenterXPercent @@ -67803,7 +70213,7 @@ This can increase performance, and also silently ignore critical errors - ChannelWidth + CenterYPercent @@ -67845,21 +70255,21 @@ This can increase performance, and also silently ignore critical errors - HGap + NoResponse - Int + Switch - Int + Switch - NoResponse + PassThru @@ -67873,35 +70283,35 @@ This can increase performance, and also silently ignore critical errors - PassThru + Power - Switch + Float - Switch + Float - ShaderText + Rotation - String + Float - String + Float - SourceName + ShaderText @@ -67915,28 +70325,28 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + SourceName - Switch + String - Switch + String - VGap + UseShaderTime - Int + Switch - Int + Switch @@ -67964,11 +70374,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTwistShader - OBSTwistShader + Get-OBSTwoPassDropShadowShader + OBSTwoPassDropShadowShader Get - Get-OBSTwistShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-Power] <float>] [[-Rotation] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSTwoPassDropShadowShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -67976,37 +70386,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTwistShader + Get-OBSTwoPassDropShadowShader - CenterXPercent + ViewProj - Int + System.Single[][] - Int + System.Single[][] - CenterYPercent + Image - Int + String - Int + String - Power + ElapsedTime @@ -68020,7 +70430,49 @@ This can increase performance, and also silently ignore critical errors - Rotation + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + RandF @@ -68033,7 +70485,91 @@ This can increase performance, and also silently ignore critical errors - + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ShadowOffsetX + + + + + Int + + Int + + + + + + + ShadowOffsetY + + + + + Int + + Int + + + + + + + ShadowBlurSize + + + + + Int + + Int + + + + + + + ShadowColor + + + + + String + + String + + + + + + + IsAlphaPremultiplied + + + + + Switch + + Switch + + + + + + SourceName @@ -68047,7 +70583,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -68061,7 +70597,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -68135,21 +70671,133 @@ This can increase performance, and also silently ignore critical errors - CenterXPercent + ElapsedTime + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Image + + + + + String + + String + + + + + + + IsAlphaPremultiplied + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + RandF + + + + + Float + + Float + + + + + + + ShaderText - Int + String - Int + String - CenterYPercent + ShadowBlurSize @@ -68163,7 +70811,7 @@ This can increase performance, and also silently ignore critical errors - FilterName + ShadowColor @@ -68177,35 +70825,49 @@ This can increase performance, and also silently ignore critical errors - Force + ShadowOffsetX - Switch + Int - Switch + Int - NoResponse + ShadowOffsetY - Switch + Int - Switch + Int - PassThru + SourceName + + + + + String + + String + + + + + + + UseShaderTime @@ -68219,70 +70881,70 @@ This can increase performance, and also silently ignore critical errors - Power + UvOffset - Float + System.Single[] - Float + System.Single[] - Rotation + UvPixelInterval - Float + System.Single[] - Float + System.Single[] - ShaderText + UvScale - String + System.Single[] - String + System.Single[] - SourceName + UvSize - String + System.Single[] - String + System.Single[] - UseShaderTime + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] @@ -68310,11 +70972,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTwoPassDropShadowShader - OBSTwoPassDropShadowShader + Get-OBSVCRShader + OBSVCRShader Get - Get-OBSTwoPassDropShadowShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVCRShader [[-VerticalShift] <float>] [[-Distort] <float>] [[-Vignet] <float>] [[-Stripe] <float>] [[-VerticalFactor] <float>] [[-VerticalHeight] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -68322,37 +70984,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSTwoPassDropShadowShader + Get-OBSVCRShader - ViewProj + VerticalShift - System.Single[][] + Float - System.Single[][] + Float - Image + Distort - String + Float - String + Float - ElapsedTime + Vignet @@ -68366,49 +71028,35 @@ This can increase performance, and also silently ignore critical errors - UvOffset + Stripe - System.Single[] + Float - System.Single[] + Float - UvScale + VerticalFactor - System.Single[] + Float - System.Single[] + Float - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - RandF + VerticalHeight @@ -68421,91 +71069,7 @@ This can increase performance, and also silently ignore critical errors - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ShadowOffsetX - - - - - Int - - Int - - - - - - - ShadowOffsetY - - - - - Int - - Int - - - - - - - ShadowBlurSize - - - - - Int - - Int - - - - - - - ShadowColor - - - - - String - - String - - - - - - - IsAlphaPremultiplied - - - - - Switch - - Switch - - - - - - + SourceName @@ -68519,7 +71083,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -68533,7 +71097,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -68607,7 +71171,7 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + Distort @@ -68648,34 +71212,6 @@ This can increase performance, and also silently ignore critical errors - - Image - - - - - String - - String - - - - - - - IsAlphaPremultiplied - - - - - Switch - - Switch - - - - - NoResponse @@ -68704,20 +71240,6 @@ This can increase performance, and also silently ignore critical errors - - RandF - - - - - Float - - Float - - - - - ShaderText @@ -68733,186 +71255,224 @@ This can increase performance, and also silently ignore critical errors - ShadowBlurSize + SourceName - Int + String - Int + String - ShadowColor + Stripe - String + Float - String + Float - ShadowOffsetX + UseShaderTime - Int + Switch - Int + Switch - ShadowOffsetY + VerticalFactor - Int + Float - Int + Float - SourceName + VerticalHeight - String + Float - String + Float - UseShaderTime + VerticalShift - Switch + Float - Switch + Float - UvOffset + Vignet - System.Single[] + Float - System.Single[] + Float - - UvPixelInterval - - - - - System.Single[] + + + - System.Single[] + System.String + - - - - - UvScale - - - - - System.Single[] + + + + - System.Single[] + System.Object + - - - + + + + + + Get-OBSVersion + OBSVersion + Get + + Get-OBSVersion : GetVersion + + 0.2.0.1 + + + Gets data about the current plugin and RPC version. + Get-OBSVersion calls the OBS WebSocket with a request of type GetVersion. + + + + Get-OBSVersion + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + - UvSize + NoResponse - - + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors - System.Single[] + Switch - System.Single[] + Switch - ViewProj + PassThru - - + If set, will return the information that would otherwise be sent to OBS. - System.Single[][] + Switch - System.Single[][] + Switch - - - - System.String - - - - - - - - - System.Object - - - - - + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSVersion + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getversion + + - Get-OBSVCRShader - OBSVCRShader + Get-OBSVHSShader + OBSVHSShader Get - Get-OBSVCRShader [[-VerticalShift] <float>] [[-Distort] <float>] [[-Vignet] <float>] [[-Stripe] <float>] [[-VerticalFactor] <float>] [[-VerticalHeight] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVHSShader [[-Range] <float>] [[-OffsetIntensity] <float>] [[-NoiseQuality] <float>] [[-NoiseIntensity] <float>] [[-ColorOffsetIntensity] <float>] [[-AlphaPercentage] <float>] [[-ColorToReplace] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -68920,9 +71480,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVCRShader + Get-OBSVHSShader - VerticalShift + Range @@ -68936,7 +71496,7 @@ This can increase performance, and also silently ignore critical errors - Distort + OffsetIntensity @@ -68950,7 +71510,7 @@ This can increase performance, and also silently ignore critical errors - Vignet + NoiseQuality @@ -68964,7 +71524,7 @@ This can increase performance, and also silently ignore critical errors - Stripe + NoiseIntensity @@ -68978,7 +71538,7 @@ This can increase performance, and also silently ignore critical errors - VerticalFactor + ColorOffsetIntensity @@ -68992,7 +71552,7 @@ This can increase performance, and also silently ignore critical errors - VerticalHeight + AlphaPercentage @@ -69005,8 +71565,36 @@ This can increase performance, and also silently ignore critical errors - - SourceName + + ApplyToImage + + + + + Switch + + Switch + + + + + + + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + ColorToReplace @@ -69019,7 +71607,35 @@ This can increase performance, and also silently ignore critical errors + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + + SourceName + + + + + String + + String + + + + + + FilterName @@ -69033,7 +71649,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -69107,7 +71723,7 @@ This can increase performance, and also silently ignore critical errors - Distort + AlphaPercentage @@ -69121,21 +71737,21 @@ This can increase performance, and also silently ignore critical errors - FilterName + ApplyToImage - String + Switch - String + Switch - Force + ApplyToSpecificColor @@ -69149,35 +71765,35 @@ This can increase performance, and also silently ignore critical errors - NoResponse + ColorOffsetIntensity - Switch + Float - Switch + Float - PassThru + ColorToReplace - Switch + String - Switch + String - ShaderText + FilterName @@ -69191,21 +71807,21 @@ This can increase performance, and also silently ignore critical errors - SourceName + Force - String + Switch - String + Switch - Stripe + NoiseIntensity @@ -69219,7 +71835,21 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + NoiseQuality + + + + + Float + + Float + + + + + + + NoResponse @@ -69233,7 +71863,7 @@ This can increase performance, and also silently ignore critical errors - VerticalFactor + OffsetIntensity @@ -69247,21 +71877,21 @@ This can increase performance, and also silently ignore critical errors - VerticalHeight + PassThru - Float + Switch - Float + Switch - VerticalShift + Range @@ -69275,14 +71905,56 @@ This can increase performance, and also silently ignore critical errors - Vignet + ReplaceImageColor - Float + Switch - Float + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch @@ -69310,21 +71982,22 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVersion - OBSVersion + Get-OBSVideoSettings + OBSVideoSettings Get - Get-OBSVersion : GetVersion + Get-OBSVideoSettings : GetVideoSettings 0.2.0.1 - Gets data about the current plugin and RPC version. - Get-OBSVersion calls the OBS WebSocket with a request of type GetVersion. + Gets the current video settings. + Note: To get the true FPS value, divide the FPS numerator by the FPS denominator. Example: `60000/1001` + Get-OBSVideoSettings calls the OBS WebSocket with a request of type GetVideoSettings. - Get-OBSVersion + Get-OBSVideoSettings PassThru @@ -69389,7 +72062,7 @@ This can increase performance, and also silently ignore critical errors PS > - Get-OBSVersion + Get-OBSVideoSettings @@ -69398,17 +72071,17 @@ This can increase performance, and also silently ignore critical errors - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getversion + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvideosettings - Get-OBSVHSShader - OBSVHSShader + Get-OBSVignettingShader + OBSVignettingShader Get - Get-OBSVHSShader [[-Range] <float>] [[-OffsetIntensity] <float>] [[-NoiseQuality] <float>] [[-NoiseIntensity] <float>] [[-ColorOffsetIntensity] <float>] [[-AlphaPercentage] <float>] [[-ColorToReplace] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVignettingShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-Opacity] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -69416,9 +72089,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVHSShader + Get-OBSVignettingShader - Range + InnerRadius @@ -69432,7 +72105,7 @@ This can increase performance, and also silently ignore critical errors - OffsetIntensity + OuterRadius @@ -69446,7 +72119,7 @@ This can increase performance, and also silently ignore critical errors - NoiseQuality + Opacity @@ -69460,77 +72133,7 @@ This can increase performance, and also silently ignore critical errors - NoiseIntensity - - - - - Float - - Float - - - - - - - ColorOffsetIntensity - - - - - Float - - Float - - - - - - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ReplaceImageColor - - - - - Switch - - Switch - - - - - - - ColorToReplace + Notes @@ -69543,21 +72146,7 @@ This can increase performance, and also silently ignore critical errors - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - + SourceName @@ -69571,7 +72160,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -69585,7 +72174,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -69658,76 +72247,6 @@ This can increase performance, and also silently ignore critical errors - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorOffsetIntensity - - - - - Float - - Float - - - - - - - ColorToReplace - - - - - String - - String - - - - - FilterName @@ -69757,21 +72276,7 @@ This can increase performance, and also silently ignore critical errors - NoiseIntensity - - - - - Float - - Float - - - - - - - NoiseQuality + InnerRadius @@ -69799,35 +72304,35 @@ This can increase performance, and also silently ignore critical errors - OffsetIntensity + Notes - Float + String - Float + String - PassThru + Opacity - Switch + Float - Switch + Float - Range + OuterRadius @@ -69841,7 +72346,7 @@ This can increase performance, and also silently ignore critical errors - ReplaceImageColor + PassThru @@ -69918,22 +72423,21 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVideoSettings - OBSVideoSettings + Get-OBSVirtualCamStatus + OBSVirtualCamStatus Get - Get-OBSVideoSettings : GetVideoSettings + Get-OBSVirtualCamStatus : GetVirtualCamStatus 0.2.0.1 - Gets the current video settings. - Note: To get the true FPS value, divide the FPS numerator by the FPS denominator. Example: `60000/1001` - Get-OBSVideoSettings calls the OBS WebSocket with a request of type GetVideoSettings. + Gets the status of the virtualcam output. + Get-OBSVirtualCamStatus calls the OBS WebSocket with a request of type GetVirtualCamStatus. - Get-OBSVideoSettings + Get-OBSVirtualCamStatus PassThru @@ -69998,7 +72502,7 @@ This can increase performance, and also silently ignore critical errors PS > - Get-OBSVideoSettings + Get-OBSVirtualCamStatus @@ -70007,17 +72511,17 @@ This can increase performance, and also silently ignore critical errors - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvideosettings + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvirtualcamstatus - Get-OBSVignettingShader - OBSVignettingShader + Get-OBSVoronoiPixelationShader + OBSVoronoiPixelationShader Get - Get-OBSVignettingShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-Opacity] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVoronoiPixelationShader [[-PixH] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Alternative] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -70025,37 +72529,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVignettingShader + Get-OBSVoronoiPixelationShader - InnerRadius - - - - - Float - - Float - - - - - - - OuterRadius - - - - - Float - - Float - - - - - - - Opacity + PixH @@ -70068,21 +72544,21 @@ This can increase performance, and also silently ignore critical errors - - Notes + + Alternative - String + Switch - String + Switch - + SourceName @@ -70096,7 +72572,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -70110,7 +72586,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -70184,21 +72660,7 @@ This can increase performance, and also silently ignore critical errors - FilterName - - - - - String - - String - - - - - - - Force + Alternative @@ -70212,21 +72674,21 @@ This can increase performance, and also silently ignore critical errors - InnerRadius + FilterName - Float + String - Float + String - NoResponse + Force @@ -70240,35 +72702,35 @@ This can increase performance, and also silently ignore critical errors - Notes + NoResponse - String + Switch - String + Switch - Opacity + PassThru - Float + Switch - Float + Switch - OuterRadius + PixH @@ -70281,20 +72743,6 @@ This can increase performance, and also silently ignore critical errors - - PassThru - - - - - Switch - - Switch - - - - - ShaderText @@ -70359,39 +72807,72 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVirtualCamStatus - OBSVirtualCamStatus + Get-OBSWalkingDeadPixelFixerShader + OBSWalkingDeadPixelFixerShader Get - Get-OBSVirtualCamStatus : GetVirtualCamStatus + +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] <int>] [[-ScanOffsetX] <int>] [[-ScanOffsetY] <int>] [[-ContrastThreshold] <float>] [[-MinClusterSize] <int>] [[-MaxClusterSize] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 - Gets the status of the virtualcam output. - Get-OBSVirtualCamStatus calls the OBS WebSocket with a request of type GetVirtualCamStatus. - Get-OBSVirtualCamStatus - - PassThru + Get-OBSWalkingDeadPixelFixerShader + + ScanWidth - If set, will return the information that would otherwise be sent to OBS. - Switch + Int - Switch + Int - - NoResponse + + ScanHeight + + + Int + + Int + + + + + + + ScanOffsetX + + + Int + + Int + + + + + + + ScanOffsetY + + + Int + + Int + + + + + + + ShowBorder - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors Switch @@ -70401,76 +72882,9 @@ This can increase performance, and also silently ignore critical errors - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSVirtualCamStatus - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvirtualcamstatus - - - - - - Get-OBSVoronoiPixelationShader - OBSVoronoiPixelationShader - Get - - Get-OBSVoronoiPixelationShader [[-PixH] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Alternative] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSVoronoiPixelationShader - - PixH + + ContrastThreshold - - Float @@ -70480,11 +72894,33 @@ This can increase performance, and also silently ignore critical errors + + MinClusterSize + + + Int + + Int + + + + + + + MaxClusterSize + + + Int + + Int + + + + + - Alternative + ShowGreen - - Switch @@ -70494,11 +72930,21 @@ This can increase performance, and also silently ignore critical errors - + + Bypass + + + Switch + + Switch + + + + + + SourceName - - String @@ -70508,11 +72954,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -70522,11 +72966,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -70539,8 +72981,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -70553,8 +72993,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -70567,8 +73005,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -70581,8 +73017,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -70596,10 +73030,8 @@ This can increase performance, and also silently ignore critical errors - Alternative + Bypass - - Switch @@ -70609,11 +73041,21 @@ This can increase performance, and also silently ignore critical errors + + ContrastThreshold + + + Float + + Float + + + + + FilterName - - String @@ -70626,8 +73068,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -70637,11 +73077,33 @@ This can increase performance, and also silently ignore critical errors + + MaxClusterSize + + + Int + + Int + + + + + + + MinClusterSize + + + Int + + Int + + + + + NoResponse - - Switch @@ -70654,8 +73116,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -70666,14 +73126,48 @@ This can increase performance, and also silently ignore critical errors - PixH + ScanHeight - - - Float + Int - Float + Int + + + + + + + ScanOffsetX + + + Int + + Int + + + + + + + ScanOffsetY + + + Int + + Int + + + + + + + ScanWidth + + + Int + + Int @@ -70682,8 +73176,6 @@ This can increase performance, and also silently ignore critical errors ShaderText - - String @@ -70693,11 +73185,33 @@ This can increase performance, and also silently ignore critical errors + + ShowBorder + + + Switch + + Switch + + + + + + + ShowGreen + + + Switch + + Switch + + + + + SourceName - - String @@ -70710,8 +73224,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch From 152fb4d67b580429eacf54c378448b8c0cef5a84 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 11 Nov 2025 02:53:54 +0000 Subject: [PATCH 063/266] feat: Adding /Shaders/README.md and refreshing OBS and PixelShader functionality --- allcommands.ps1 | 91778 +++++++++++++++++++++++----------------------- 1 file changed, 46821 insertions(+), 44957 deletions(-) diff --git a/allcommands.ps1 b/allcommands.ps1 index bf33fc72..9e2a119f 100644 --- a/allcommands.ps1 +++ b/allcommands.ps1 @@ -1030,425 +1030,502 @@ $($ExecutionContext.SessionState.InvokeCommand.GetCommand('Send-OBS', 'Function' } #.ExternalHelp obs-powershell-Help.xml -function Set-OBSAudioOutputSource { +function Get-OBSEffect +{ - - #> - [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] param( - # The name of the audio device. - # This name or device ID of the audio device that should be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','DeviceID')] - [string] - $AudioDevice, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - - # The name of the input. - # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. + # The name of the effect. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] + [Alias('EffectName')] [string] - $Name, - - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force + $Name ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput + + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + } + process { - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } + if (-not $Name) { + $script:OBSFX.Values + } elseif ($script:OBSFX[$name]) { + $script:OBSFX[$name] } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) } - $DynamicParameters +} +#.ExternalHelp obs-powershell-Help.xml +function Import-OBSEffect { - } - begin { - # Audio Output sources have an inputKind of 'wasapi_output_capture'. - $inputKind = "wasapi_output_capture" + param( + # The source location of the effect. + # This can be a string, file, directory, command, or module. + [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] + [Alias( + 'FromPath', + 'FromModule', + 'FromScript', + 'FromFunction', + 'FullName', + 'Path', + 'Source' + )] + [ValidateScript({ + $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + return $true + })] + + $From + ) - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } - - if (-not $myParameters["AudioDevice"]) { - $myParameters["AudioDevice"] = "default" - } + $newEffects = @() + $obsEffectsPattern = [Regex]::new(' + (?> + ^OBS.(?>fx|effects?)\p{P} + | + [\p{P}-[-]]OBS\.(?>fx|effects?)$ + | + \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ + ) + ','IgnoreCase,IgnorePatternWhitespace') + } - # Window capture is a bit of a tricky one. - # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. - # and for that, an input needs to exist. - if (-not $myParameters["Name"]) { + process { + # Since -From can be many things, but a metric has to be a command, + # the purpose of this function is to essentially resolve many things to a command, + # and then cache that command. - - if ($myParameters["AudioDevice"]) { - $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] - } - else { - $Name = $myParameters["Name"] = "AudioOutput" - } + # If -From was a string + if ($From -is [string]) { + # It could be a module, so check those first. + :ResolveFromString do { + foreach ($loadedModule in @(Get-Module)) { + # If we find the module, don't try to resolve -From as a path + if ($loadedModule.Name -eq $from) { + # (just set -From again and let the function continue) + $from = $fromModule = $loadedModule;break ResolveFromString + } + + } + # If we think from was a path + $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) + # attempt to resolve it + if ($resolvedPath) { + $from = Get-Item -LiteralPath $resolvedPath + } + } while ($false) } + # If -From is a module + if ($from -is [Management.Automation.PSModuleInfo]) { + # recursively call ourselves with all matching commands + @($from.ExportedCommands.Values) -match $obsEffectsPattern | + Import-OBSEffect + # then, make -From a directory + if ($from.Path) { + $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue + } + } + # If -From is a directory + if ($from -is [IO.DirectoryInfo]) { + $FromDirectory = $from + # recursively call ourselves with all matching scripts + Get-ChildItem -LiteralPath $from.FullName -Recurse -File | + Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | + Import-OBSEffect + return + } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + # If -From is a file + if ($from -is [IO.FileInfo]) { + # and it matches the naming convention + if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } + # make -From a command. + $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') + } - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } + # If -From is a command + if ($from -is [Management.Automation.CommandInfo]) { + # decorate the command + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect') } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + if ($from -is [Management.Automation.ApplicationInfo]) { + $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' + $newEffect = [PSCustomObject][Ordered]@{ + PSTypeName = 'OBS.PowerShell.Effect' + Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json + EffectName = $effectName + TypeName = $TypeName } - } - } + $script:OBSFX[$effectName] = $newEffect + $newEffects += $newEffect + $newEffect + } else { + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') + } + # and add it to our list of new metrics + $newEffects+= $from + $script:OBSFX[$from.EffectName] = $from + $from + } + } + } - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = $inputKind - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id - foreach ($deviceInfo in $possibleDevices) { - if ( - ($deviceInfo.itemName -eq $AudioDevice) -or - ($deviceInfo.ItemValue -eq $AudioDevice) -or - ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or - ($deviceInfo.ItemValue -like "*$AudioDevice*") -or - ($deviceInfo.ItemName -like "*$AudioDevice*") - ) { - $myParameterData["device_id"] = $deviceInfo.itemValue - break - } - } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return +} + + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string] + $EffectName + ) + + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } + } - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Scene"] - } - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + process { + if ($script:OBSFX[$name]) { + $script:OBSFX.Stop() + $script:OBSFX.Remove($name) } - } } #.ExternalHelp obs-powershell-Help.xml -function Set-OBSBrowserSource { +function Start-OBSEffect +{ - - [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] + [CmdletBinding(PositionalBinding=$false)] param( - # The uri or file path to display. - # If the uri points to a local file, this will be preferred - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url', 'Href','Path','FilePath','FullName')] - [uri] - $Uri, + # The name of the effect. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + $effectNames = @(Get-OBSEffect| + Select-Object -Unique -ExpandProperty EffectName) + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($effectNames -replace '^', "'" -replace '$',"'") + } + })] + [Parameter(Mandatory)] + [string[]] + $EffectName, - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. + # The duration of the effect. + # If provided, all effects should use this duration. + # If not provided, each effect should use it's own duration. + [Timespan] + $Duration, + + # The parameters passed to the effect. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] + [Alias('EffectParameters')] + [Collections.IDictionary] + $EffectParameter = @{}, + + # The arguments passed to the effect. + [Parameter(ValueFromRemainingArguments)] + [Alias('EffectArguments')] + [PSObject[]] + $EffectArgument = @(), + + # If provided, will step thru running + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ticks')] [int] - $Width, + $Step, - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. + # The SceneItemID. If this is provided, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] [int] - $Height, + $SceneItemID, - # The css style used to render the browser page. + # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + $SceneName, - # If set, the browser source will shutdown when it is hidden + # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, + [string] + $FilterName, - # If set, the browser source will restart when it is activated. + # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, + [string] + $SourceName, - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] + # If set, will loop the effect. [switch] - $RerouteAudio, + $Loop, - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] + # If provided, will loop the effect a number of times. [int] - $FramesPerSecond, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, + $LoopCount, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, + # If set, will bounce the effect (flip it / reverse it) + [switch] + $Bounce, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will reverse an effect. [switch] - $Force + $Reverse ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + process { + foreach ($NameOfEffect in $EffectName) { + $obsEffect = Get-OBSEffect -EffectName $NameOfEffect - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + if (-not $obsEffect) { + Write-Warning "No Effect named '$NameOfEffect'" + continue } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - $inputKind = "browser_source" + if ($LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force + } - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + if ($loop -or $Bounce) { + $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force + if (-not $LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force + } + } else { + $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force + } - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters + if ($Reverse) { + $obsEffect.Reversed = $true + } + + if ($obsEffect -isnot [Management.Automation.CommandInfo]) { + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + $obsEffect.Start() + } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + if (-not $this) { + if ($_.pstypenames -like '*.GetSourceFilter*') { + $this = $_ + } elseif ($FilterName -and $SourceName) { + $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName + } + + if ($_.pstypenames -like '*.GetSceneItem*') { + $this = $_ + } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { + $this = + foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { + if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { + $sceneItem;break + } + elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { + $sceneItem;break + } + } + } + } + + if ($Duration -and $obsEffect.Parameters.Duration) { + $EffectParameter.Duration = $Duration + } + + $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument + if ($obsEffectOutput) { + $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force + if ($step) { + $obsEffect.Step($step) + } else { + $obsEffect.Start() + } + } } - # (either way, if we were called Get- or with no verb, we're done now). - return + $obsEffect } + + + } +} +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [string] + $EffectName) - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings + process { + $obsEffect = Get-OBSEffect -EffectName $EffectName + + if (-not $obsEffect) { return } + + $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force + } +} +#.ExternalHelp obs-powershell-Help.xml +function Set-OBS3DFilter { + + + [Alias('Add-OBS3DFilter')] + param( + # The Field of View + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fov")] + [double] + $FieldOfView, + + # The Rotation along the X-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rot_x")] + [double] + $RotationX, + + # The Rotation along the Y-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rot_y")] + [double] + $RotationY, + + # The Rotation along the Z-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rot_z")] + [double] + $RotationZ, + + # The Position along the X-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("pos_x")] + [double] + $PositionX, + + # The Position along the Y-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("pos_y")] + [double] + $PositionY, + + # The Position along the Z-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("pos_z")] + [double] + $PositionZ, + + # The scale of the source along the X-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("scale_x")] + [double] + $ScaleX, + + # The scale of the source along the Y-axis + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("scale_y")] + [double] + $ScaleY, + + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter + } else { + $script:AddOBSSourceFilter + } + $IncludeParameter = @() + $ExcludeParameter = 'FilterKind','FilterSettings' + + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "3Band3D" } $myParameterData = [Ordered]@{} foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null + $bindToPropertyName = $null foreach ($attribute in $parameter.Attributes) { if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { @@ -1461,76 +1538,33 @@ function Set-OBSBrowserSource { if ($myParameters.Contains($parameter.Name)) { $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] } } } - - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true - } - if ($uri.Scheme -eq 'File') { - if (Test-Path $uri.AbsolutePath) { - $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true - } - } - else - { - if (Test-Path $uri) { - $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) - $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true - } else { - $myParameterData["url"] = "$uri" - } - } - - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($uri.Segments) { - $uri.Segments[-1] - } elseif ($uri -match '[\\/]') { - @($uri -split '[\\/]')[-1] - } else { - $uri - } - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name + + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "3d_effect_filter" + filterSettings = $myParameterData NoResponse = $myParameters["NoResponse"] } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } - + # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -1538,16 +1572,17 @@ function Set-OBSBrowserSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -1556,61 +1591,96 @@ function Set-OBSBrowserSource { if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) - } + } + } # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSColorSource { +function Set-OBSColorFilter { - [Alias('Add-OBSColorSource','Get-OBSColorSource')] + [Alias('Add-OBSColorFilter','Add-OBSColorCorrectionFilter','Set-OBSColorCorrectionFilter')] param( - # The name of the scene. - # If no scene name is provided, the current program scene will be used. + # The opacity, as a number between 0 and 1. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, + [ValidateRange(0,1)] + [ComponentModel.DefaultBindingProperty("opacity")] + [double] + $Opacity, - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + # The brightness, as a number between -1 and 1. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, + [ValidateRange(-1,1)] + [ComponentModel.DefaultBindingProperty("brightness")] + [double] + $Brightness, - [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] + # The constrast, as a number between -4 and 4. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateRange(-4,4)] + [ComponentModel.DefaultBindingProperty("contrast")] + [double] + $Contrast, + + # The gamma correction, as a number between -3 and 3. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateRange(-3,3)] + [ComponentModel.DefaultBindingProperty("gamma")] + [double] + $Gamma, + + # The saturation, as a number between -1 and 5. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateRange(-1,5)] + [ComponentModel.DefaultBindingProperty("saturation")] + [double] + $Saturation, + + # The change in hue, as represented in degrees around a color cicrle + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("hue_shift")] + [Alias('Spin')] + [double] + $Hue, + + # Multiply this color by all pixels within the source. [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_multiply")] [string] - $Color, + $MultiplyColor, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. + # Add all this color to all pixels within the source. [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_add")] + [string] + $AddColor, + + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -1638,143 +1708,87 @@ function Set-OBSColorSource { } begin { - $inputKind = "color_source_v3" + filter ToOBSColor { + + if ($_ -is [uint32]) { $_ } + elseif ($_ -is [string]) { + if ($_ -match '^\#[a-f0-9]{3,4}$') { + $_ = $_ -replace '[a-f0-9]','$0$0' + } + + if ($_ -match '^#[a-f0-9]{8}$') { + $_ -replace '#','0x' -as [UInt32] + } + elseif ($_ -match '^#[a-f0-9]{6}$') { + $_ -replace '#','0xff' -as [UInt32] + } + } + + } } process { - # Copy the bound parameters $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + if (-not $myParameters["FilterName"]) { + $FilterName = $myParameters["FilterName"] = "ColorCorrection" + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } + $bindToPropertyName = $null - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if ($myParameterData.color_add) { + $myParameterData.color_add = $myParameterData.color_add | ToOBSColor } - $hexChar = [Regex]::new('[0-9a-f]') - $hexColors = @($hexChar.Matches($Color)) + if ($myParameterData.color_multiply) { + $myParameterData.color_multiply = $myParameterData.color_multiply | ToOBSColor + } - switch ($hexColors.Length) { - 8 { - #full rgba - $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') - } - 6 { - #rgb only, assume ff for alpha - $alpha = 0xff - $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - } - 4 { - #short rgba - $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') - } - 3 { - #short rgb, assume f for alpha - $alpha = 0xff - $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - } - 0 { - # No color provided, default to transparent black - $alpha = 0 - $red = 0 - $green = 0 - $blue = 0 - } - } - - $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - - $realColor = [uint32]::Parse($hexColor,'HexNumber') - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "#$hexColor" - } - - $myParameterData = [Ordered]@{color=$realColor} - - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "color_source_v3" - inputSettings = $myParameterData + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "color_filter_v2" + filterSettings = $myParameterData NoResponse = $myParameters["NoResponse"] } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return - } + return + } # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + + if ($PassThru) { + return $outputAddedResult + } # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -1782,16 +1796,17 @@ function Set-OBSColorSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -1800,73 +1815,62 @@ function Set-OBSColorSource { if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) - } + } + } # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - } + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } #.ExternalHelp obs-powershell-Help.xml -function Set-OBSDisplaySource { +function Set-OBSEqualizerFilter { - [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] + [Alias('Add-OBSEqualizierFilter','Add-OBS3BandEqualizerFilter','Set-OBS3BandEqualizerFilter')] param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("monitor")] - [Alias('MonitorNumber','Display','DisplayNumber')] - [int] - $Monitor = 1, - - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("capture_cursor")] - [switch] - $CaptureCursor, + # The change in low frequencies. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("low")] + [ValidateRange(-20,20)] + [double] + $Low, - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, + # The change in mid frequencies. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("mid")] + [ValidateRange(-20,20)] + [double] + $Mid, - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, + # The change in high frequencies. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("high")] + [ValidateRange(-20,20)] + [double] + $High, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -1896,9 +1900,8 @@ function Set-OBSDisplaySource { process { $myParameters = [Ordered]@{} + $PSBoundParameters - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "3BandEqualizer" } $myParameterData = [Ordered]@{} @@ -1921,48 +1924,28 @@ function Set-OBSDisplaySource { } } } - - # Users like 1 indexed, computers like zero-indexed. - $myParameterData["monitor"] = $Monitor - 1 - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "Display $($Monitor)" - } - - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "monitor_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "basic_eq_filter" + filterSettings = $myParameterData } - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -1970,16 +1953,17 @@ function Set-OBSDisplaySource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -1988,79 +1972,48 @@ function Set-OBSDisplaySource { if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) - } + } + } # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMarkdownSource { +function Set-OBSGainFilter { - [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] + [Alias('Add-OBSGainFilter')] param( - # The markdown text, or the path to a markdown file - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Markdown, - - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, - - # The css style used to render the markdown. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, + # The Audio Gain, in decibels. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("db")] + [double] + $Gain, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -2086,129 +2039,35 @@ function Set-OBSMarkdownSource { } $DynamicParameters - } - begin { - $inputKind = "markdown_source" - } process { $myParameters = [Ordered]@{} + $PSBoundParameters - - $IsGet = $MyInvocation.InvocationName -like "Get-*" - $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - if ( - $IsGet -or - ($NoVerb -and -not $NonNameParameters) - ) { - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } - - $markdownAsUri = $null - if ($Markdown -like '*.md') { - $markdownAsUri = $markdown -as [uri] - if ($markdownAsUri.Scheme -eq 'File') { - $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["markdown_source"] = 1 - } - else { - - } - } else { - $myParameterData["text"] = $Markdown - $myParameterData["markdown_source"] = 0 + + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "Gain" } - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($markdownAsUri.Segments) { - $markdownAsUri.Segments[-1] - } elseif ($markdownAsUri -match '[\\/]') { - @($markdownAsUri -split '[\\/]')[-1] - } elseif ($markdownAsUri) { - $markdownAsUri - } else { - "Markdown" - } - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = "markdown_source" - inputSettings = $myParameterData - inputName = $Name + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "gain_filter" + filterSettings = [Ordered]@{db=$Gain} NoResponse = $myParameters["NoResponse"] } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # If -PassThru was passed + if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } - + # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -2216,16 +2075,17 @@ function Set-OBSMarkdownSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -2234,104 +2094,48 @@ function Set-OBSMarkdownSource { if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) - } + } + } # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaSource { +function Set-OBSRenderDelayFilter { - [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] + [Alias('Add-OBSRenderDelayFilter')] param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file')] - [string] - $FilePath, - - # If set, the source will close when it is inactive. - # By default, this will be set to true. - # To explicitly set it to false, use -CloseWhenInactive:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("close_when_inactive")] - [switch] - $CloseWhenInactive, - - # If set, the source will automatically restart. + # The RenderDelay. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("looping")] - [Alias('Looping')] - [switch] - $Loop, + [timespan] + $RenderDelay, - # If set, will use hardware decoding, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("hw_decode")] - [Alias('HardwareDecoding','hw_decode')] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] - $UseHardwareDecoding, - - # If set, will clear the output on the end of the media. - # If this is set to false, the media will freeze on the last frame. - # This is set to true by default. - # To explicitly set to false, use -ClearMediaEnd:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("clear_on_media_end")] - [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] - [switch] - $ClearOnMediaEnd, - - # Any FFMpeg demuxer options. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ffmpeg_options")] - [Alias('FFMpegOptions', 'FFMpeg_Options')] - [string] - $FFMpegOption, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, - - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, - - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen + $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -2357,155 +2161,43 @@ function Set-OBSMediaSource { } $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - - } - $InputKind = "ffmpeg_source" - } process { - # Copy the bound parameters $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "RenderDelay" } - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } - - if ((Test-Path $FilePath)) { - $FilePathItem = Get-Item -Path $FilePath - $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' - } - - - - if ($myParameters['InputSettings']) { - $keys = - @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { - $myParameters['InputSettings'].Keys - } else { - foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { - $prop.Name - } - }) - - foreach ($key in $keys) { - $myParameterData[$key] = $myParameters['InputSettings'].$key + + $myParameterData = [Ordered]@{ + delay_ms = if ($RenderDelay.Ticks -lt 10kb) { + [int]$RenderDelay.Ticks + } else { + [int]$RenderDelay.TotalMilliseconds } - - $myParameterData.remove('inputSettings') - } - - if (-not $Name) { - - $Name = $myParameters["Name"] = - if ($FilePathItem.Name) { - $FilePathItem.Name - } else { - "Media" - } - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "gpu_delay" + filterSettings = $myParameterData NoResponse = $myParameters["NoResponse"] } - - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # If -PassThru was passed + if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -2513,16 +2205,17 @@ function Set-OBSMediaSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -2532,152 +2225,215 @@ function Set-OBSMediaSource { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) } + } - # Otherwise, if we had a result - if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSoundCloudSource { +function Set-OBSScaleFilter { - [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] + [Alias('Add-OBSScaleFilter')] param( - # The uri to display. This must point to a SoundCloud URL. + # The Resolution. Can either width x height (e.g. 1920x1080) or an aspect ratio (16:9). [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url','SoundCloudUri','SoundCloudUrl')] - [uri] - $Uri, - - # If set, will not autoplay. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoAutoPlay, + [ComponentModel.DefaultBindingProperty("resolution")] + [Alias('Scale')] + [string] + $Resolution, - # If set, will not display album artwork. + # The sampling method. It will default to "lanczos". [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoArtwork, + [ComponentModel.DefaultBindingProperty("sampling")] + [string] + $Sampling = 'lanczos', - # If set, will not display play count. + # If set, will keep the aspect ratio when scaling. + # This is only valid if the sampling method is set to "lanczos". [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("undistort")] + [Alias('Undistort')] [switch] - $NoPlayCount, + $KeepAspectRatio, - # If set, will not display uploader info. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] - $NoUploaderInfo, - - # If provided, will start playing at a given track number. - [Parameter(ValueFromPipelineByPropertyName)] - [int] - $TrackNumber, + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter + } else { + $script:AddOBSSourceFilter + } + $IncludeParameter = @() + $ExcludeParameter = 'FilterKind','FilterSettings' - # If set, will show a share link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowShare, - # If set, will show a download link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowDownload, + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # If set, will show a buy link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowBuy, + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "Scale" + } + + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "scale_filter" + filterSettings = [Ordered]@{resolution=$Resolution;sampling=$Sampling;undistort=$KeepAspectRatio -as [bool]} + NoResponse = $myParameters["NoResponse"] + } + + if ($MyParameters["PassThru"]) { + $addSplat.Passthru = $MyParameters["PassThru"] + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSSourceFilter @addSplat + } else { + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat + } + return + } - # The color used for the SoundCloud audio bars and buttons. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Color, + # Add the input. + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) + $outputAddedResult = $null + } + } - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + + } + + } +} - # The css style used to render the browser page. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", - # If set, the browser source will shutdown when it is hidden - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSScrollFilter { + + + param( + # The horizontal scroll speed. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("speed_x")] + [Alias('SpeedX', 'Speed_X','HSpeed')] + [double] + $HorizontalSpeed, - # If set, the browser source will restart when it is activated. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, + # The vertical scroll speed. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("speed_y")] + [Alias('SpeedY', 'Speed_Y','VSpeed')] + [double] + $VerticalSpeed, - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] + # If set, will not loop + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] [switch] - $RerouteAudio, - - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] - [int] - $FramesPerSecond, + $NoLoop, - # The name of the scene. - # If no scene name is provided, the current program scene will be used. + # If provided, will limit the width. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, + [ValidateRange(-500, 500)] + [ComponentModel.DefaultBindingProperty("cx")] + [Alias('LimitX', 'Limit_CX','WidthLimit')] + [double] + $LimitWidth, - # The name of the input. - # If no name is provided, then "SoundCloud" will be used. + # If provided, will limit the height. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, + [ValidateRange(-500, 500)] + [ComponentModel.DefaultBindingProperty("cy")] + [Alias('LimitY', 'Limit_CY','HeightLimit')] + [double] + $LimitHeight, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -2704,87 +2460,17 @@ function Set-OBSSoundCloudSource { $DynamicParameters } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - # Sound Cloud Sources are really Browser Sources. - $inputKind = "browser_source" - - } - process { - # Copy the bound parameters + process { $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - - if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { - Write-Error "URI must be from SoundCloud.com" - return - } - - if ($uri.Query) { - $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') - } - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } | - Where-Object { - $_.Settings['LocalFile'] -like '*.SoundCloud.*' - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "Scroll" } $myParameterData = [Ordered]@{} foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null + $bindToPropertyName = $null foreach ($attribute in $parameter.Attributes) { if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { @@ -2797,97 +2483,42 @@ function Set-OBSSoundCloudSource { if ($myParameters.Contains($parameter.Name)) { $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] } } } - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true + if ($myParameterData["loop"]) { + $myParameterData["loop"] = -not $myParameterData["loop"] } - - $MyObsPowerShellPath = if ($home) { - Join-Path $home ".obs-powershell" + if ($myParameterData["cx"]) { + $myParameterData["limit_cx"] = $true + } + if ($myParameterData["cy"]) { + $myParameterData["limit_cy"] = $true } - - $ThisSoundCloudSourceFileName = - if ($name) { - "${name}.SoundCloudSource.html" - } else { - "SoundCloudSource.html" - } - - $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName - - - $soundCloudSrc = @( - "https://w.soundcloud.com/player/?url=" - $Uri - "&" - @( - if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} - if ($color) { "color=$color" -replace "\#",'%23'} - if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} - if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } - if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} - if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } - if ($ShowDownload) { "download=true"} else { "download=false" } - if ($ShowBuy) { "buying=true"} else { "buying=false" } - if ($ShowShare) { "sharing=true"} else { "sharing=false" } - ) -join '&' - ) -join '' - - $soundCloudWidget = @( - "" - "" - "" - "" - "" - ) -join ' ' - - $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force - $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true - - if (-not $Name) { - $Name = $myParameters['Name'] = 'SoundCloud' - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "scroll_filter" + filterSettings = $myParameterData NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + } - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } - + # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -2895,16 +2526,17 @@ function Set-OBSSoundCloudSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -2913,226 +2545,59 @@ function Set-OBSSoundCloudSource { if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) - } + } + } # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSwitchSource { +function Set-OBSShaderFilter { - [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] + [Alias('Add-OBSShaderFilter')] param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Sources')] - [string[]] - $SourceList, - - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." - } - return $true - })] - - [Alias('SelectIndex','SelectName')] - $Select, - - # If set, the list of sources will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, - - # If set, will switch between sources. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch")] - [switch] - $TimeSwitch, - - # The interval between sources - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_between")] - [timespan] - $Interval, - - # The duration between sources that are switching at a time. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_duration")] - [timespan] - $Duration, - - # The item that will be switched in a TimeSwitch, after -Duration and -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $TimeSwitchTo = "Next", - - # If set, will switch on the underlying source's media state events. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("media_state_switch")] - [switch] - $MediaStateSwitch, - - # The change in media state that should trigger a switch - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] - $MediaStateChange, - - # When the source switcher is trigger by media end, this determines the next source that will be switched to. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $MediaSwitchTo = "Next", - - # The name of the transition between sources. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $TransitionName, - - # The properties sent to the transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $TransitionProperty, - - # The name of the transition used to show a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $ShowTransition, - - # The properties sent to the show transition. - # Notice: this current requires confirmation in the UI. + # The text of the shader [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $ShowTransitionProperty, + [string]$ShaderText, - # The transition used to hide a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] + # The file path to the shader, or the short file name of the shader. [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ShaderName')] [string] - $HideTransition, + $ShaderFile, - # The properties sent to the hide transition. - # Notice: this current requires confirmation in the UI. + # Any other settings for the shader. + # To see what the name of a shader setting is, change it in the user interface and then get the input's filters. [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ShaderSettings')] [PSObject] - $HideTransitionProperty, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, - - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, + $ShaderSetting, - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] - $FitToScreen + $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -3158,223 +2623,82 @@ function Set-OBSSwitchSource { } $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - - } - $InputKind = "source_switcher" - } process { - # Copy the bound parameters $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "Shader" } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + $shaderSettings = [Ordered]@{} + if ($ShaderText) { + $shaderSettings.shader_text = $ShaderText + } + elseif ($ShaderFile) { + if ($ShaderFile -match '[\\/]') { + $shaderSettings.shader_file_name = "$($ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($ShaderFile))" -replace "\\", "/" + } else { + if (-not $script:CachedOBSShaderFilters) { + $script:CachedOBSShaderFilters = + Get-OBS | # Get the OBS object + Select-Object -ExpandProperty Process | # which has a process + Split-Path | Split-Path | Split-Path | # from it's parent path, we go up three levels. + Join-Path -ChildPath data | Join-Path -ChildPath obs-plugins | # then down into plugin data. + Get-ChildItem -Filter obs-shaderfilter | + Get-ChildItem -Filter examples | + Get-ChildItem -File # get all of the files in this directory - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } - if ($myParameters[$parameter.Name] -is [timespan]) { - $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds + + $foundShaderFile = $script:CachedOBSShaderFilters | + Where-Object Name -Like "$shaderFile*" | + Select-Object -First 1 + + if ($foundShaderFile) { + $shaderSettings.shader_file_name = $foundShaderFile.FullName -replace "\\", "/" } } } - - - $selectedIndex = -1 - $sourcesObject = @( - $currentIndex = -1 - foreach ($sourceName in $SourceList) { - $currentIndex++ - $selected = ($null -ne $Select) -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [string] -and - ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) - ) - ) - if ($selected) { - $selectedIndex = $currentIndex - } - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} - } - ) - if ($sourcesObject) { - $myParameterData['sources'] = $sourcesObject - if ($selectedIndex -gt 0) { - $myParameterData["current_index"] = $selectedIndex + if ($shaderSetting) { + if ($shaderSetting -is [Collections.IDictionary]) { + foreach ($kv in $shaderSetting.GetEnumerator()) { + $shaderSettings[$kv.Key] = $kv.Value + } + } elseif ($shaderSetting -is [psobject]) { + foreach ($prop in $shaderSetting.psobject.properties) { + $shaderSettings[$prop.Name] = $prop.Value + } } } - elseif ($Select -is [int]) { - $myParameterData['current_index'] = $Select - } - - - - if ($TransitionName) { - if ($TransitionName -notlike '*_transition') { - $TransitionName = "${TransitionName}_transition" - } - $myParameterData["transition"] = $TransitionName - } - - if ($TransitionProperty) { - $myParameterData["transition_properties"] = $TransitionProperty - } - - if ($ShowTransition) { - if ($ShowTransition -notlike '*_transition') { - $ShowTransition = "${ShowTransition}_transition" - } - $myParameterData["show_transition"] = $ShowTransition - } - - if ($ShowTransitionProperty) { - $myParameterData["show_transition_properties"] = $ShowTransitionProperty - } - - if ($HideTransition) { - if ($HideTransition -notlike '*_transition') { - $HideTransition = "${HideTransition}_transition" - } - $myParameterData["hide_transition"] = $ShowTransition - } - - if ($HideTransitionProperty) { - $myParameterData["hide_transition_properties"] = $HideTransitionProperty - } - - if ($TimeSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["time_switch_to"] = $vvi - break - } - } - } - if ($MediaSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_state_switch_to"] = $vvi - break - } - } - } - - if ($MediaStateChange) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_switch_state"] = $vvi - break - } - } - } - - if (-not $Name) { - $Name = $myParameters["Name"] = "Source Switcher" + if ($shaderSettings.shader_file_name) { + $shaderSettings.from_file = $true } - + - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "shader_filter" + filterSettings = $shaderSettings NoResponse = $myParameters["NoResponse"] - } - - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + } - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + # Add the filter. + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -3382,19 +2706,18 @@ function Set-OBSSwitchSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - if ($sceneItem) { - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) + $outputAddedResult = $null } } @@ -3403,123 +2726,49 @@ function Set-OBSSwitchSource { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) } + } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSVLCSource { +function Set-OBSSharpnessFilter { - [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] + [Alias('Add-OBSSharpnessFilter')] param( - # The path to the media file. + # The Sharpness. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file','Playlist')] - [string[]] - $FilePath, - - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - # If an `[IO.FileInfo]` is provided, this will be the exact file. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." - } - return $true - })] - - [Alias('SelectIndex','SelectName')] - $Select, - - # If set, will shuffle the playlist - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shuffle")] - [switch] - $Shuffle, - - # If set, the playlist will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, - - # If set, will show subtitles, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle_enable")] - [Alias('ShowSubtitles','Subtitles')] - [switch] - $Subtitle, - - # The selected audio track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("track")] - [int] - $AudioTrack, - - # The selected subtitle track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle")] - [int] - $SubtitleTrack, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, - - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, + [ComponentModel.DefaultBindingProperty("sharpness")] + [ValidateRange(0,1)] + [double] + $Sharpness, - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] + # If set, will remove a filter if one already exists. + # If this is not provided and the filter already exists, the settings of the filter will be changed. [switch] - $FitToScreen + $Force ) dynamicParam { $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput + if (-not $script:AddOBSSourceFilter) { + $script:AddOBSSourceFilter = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') + $script:AddOBSSourceFilter } else { - $script:AddOBSInput + $script:AddOBSSourceFilter } $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $ExcludeParameter = 'FilterKind','FilterSettings' $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() @@ -3545,65 +2794,12 @@ function Set-OBSVLCSource { } $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - - } - $InputKind = "vlc_source" - } process { - # Copy the bound parameters $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $myParameters["FilterName"]) { + $filterName = $myParameters["FilterName"] = "Sharpness" } $myParameterData = [Ordered]@{} @@ -3622,68 +2818,32 @@ function Set-OBSVLCSource { if ($myParameters.Contains($parameter.Name)) { $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] } } - } + } - $allPaths = @(foreach ($path in $FilePath) { - foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { - $_.Path - } - }) - $playlistObject = @( - $currentIndex = 0 - foreach ($path in $allPaths) { - $currentIndex++ - $selected = $Select -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or - ($select -is [string] -and - ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) - ) - ) - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} - } - ) - $myParameterData['playlist'] = $playlistObject - - if (-not $Name) { - $Name = $myParameters["Name"] = $FilePathItem.Name - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name + $addSplat = @{ + filterName = $myParameters["FilterName"] + SourceName = $myParameters["SourceName"] + filterKind = "Sharpness_filter" + filterSettings = $myParameterData NoResponse = $myParameters["NoResponse"] - } - - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + } - # If -PassThru was passed if ($MyParameters["PassThru"]) { - # pass it down to each command $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput + Add-OBSSourceFilter @addSplat } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $addSplat.Remove('FilterKind') + Set-OBSSourceFilterSettings @addSplat } - return + return } # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 # If we got back an error if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { @@ -3691,16 +2851,17 @@ function Set-OBSVLCSource { if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { # then check if we use the -Force. if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName + Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName # and re-add our result. $outputAddedResult = Add-OBSInput @addSplat *>&1 } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + # Otherwise, get the existing filter. + $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + # then apply the settings + $existingFilter.Set($addSplat.filterSettings) + # and output them + $existingFilter + # (don't forget to null the result, so we don't show this error) $outputAddedResult = $null } } @@ -3710,1003 +2871,724 @@ function Set-OBSVLCSource { # use $psCmdlet.WriteError so that it shows the error correctly. $psCmdlet.WriteError($outputAddedResult) } + } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen + elseif ($outputAddedResult) { + # Otherwise, get the input from the filters. + Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName + } } } + + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSWaveformSource { - - - [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] - param( - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, +function Add-OBSInput { - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, - # The audio source for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("audio_source")] - [string] - $AudioSource, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] +[Alias('obs.powershell.websocket.CreateInput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - # The display mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("display_mode")] - [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] - [string] - $DisplayMode, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - # The render mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("line","solid","gradient")] - [string] - $RenderMode, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - # The windowing mode for the waveform. - # This is the mathematical function used to determine the current "window" of audio data. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("hann","hamming","blackman","blackman_harris","none")] - [string] - $WindowMode, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - # The color used for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_base")] - [PSObject] - $Color, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, - # The crest color used for the waveform. - # This will be ignored if the render mode is not "gradient". - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_crest")] - [PSObject] - $CrestColor, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, - # The channel mode for the waveform. - # This can be either mono or stereo. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_mode")] - [ValidateSet("mono","stereo")] - [string] - $ChannelMode, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # The number of pixels between each channel in stereo mode - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_spacing")] - [int] - $ChannelSpacing, - # If set, will use a radial layout for the waveform - # Radial layouts will ignore the desired height of the source and instead create a square. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("radial_layout")] - [switch] - $RadialLayout, +process { - # If set, will invert the direction for a radial waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("invert_direction")] - [switch] - $InvertRadialDirection, - - # If set, will normalize the volume displayed in the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("normalize_volume")] - [switch] - $NoramlizeVolume, - - # If set, will automatically declare an FFTSize - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("auto_fft_size")] - [switch] - $AutoFftSize, - - # If set, will attempt to make audio peaks render faster. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fast_peaks")] - [switch] - $FastPeak, - # The width of the waveform bar. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_width")] - [int] - $BarWidth, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # The gap between waveform bars. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_gap")] - [int] - $BarGap, + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - # The width of waveform bar step. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_width")] - [int] - $StepWidth, + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - # The gap between waveform bar steps. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_gap")] - [int] - $StepGap, + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # The low-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_low")] - [int] - $LowCutoff, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # The high-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_high")] - [int] - $HighCutoff, +} - # The floor of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("floor")] - [int] - $Floor, - # The ceiling of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ceiling")] - [int] - $Ceiling, +} - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("slope")] - [double] - $Slope, + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSProfile { - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_q")] - [Alias('RollOffOctaves')] - [double] - $RollOffOctave, - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_rate")] - [double] - $RollOffRate, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] +[Alias('obs.powershell.websocket.CreateProfile')] +param( - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("grad_ratio")] - [double] - $GradientRatio, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("deadzone")] - [double] - $Deadzone, - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("temporal_smoothing")] - [ValidateSet("none","exp_moving_avg")] - [string] - $TemporalSmoothing, +process { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' - - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - if (-not $shouldInclude) { continue nextInputParameter } + } } - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - } - begin { - $inputKind = "phandasm_waveform_source" - filter ToOBSColor { - - if ($_ -is [uint32]) { $_ } - elseif ($_ -is [string]) { - if ($_ -match '^\#[a-f0-9]{3,4}$') { - $_ = $_ -replace '[a-f0-9]','$0$0' - } - - if ($_ -match '^#[a-f0-9]{8}$') { - ( - '0x' + - (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') - ) -as [UInt32] - } - elseif ($_ -match '^#[a-f0-9]{6}$') { - - ( - '0xff' + - (($_ -replace '#').ToCharArray()[-1..-6] -join '') - ) -as [UInt32] - } - } - + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - $MyInvocationName = "$($MyInvocation.InvocationName)" - $myVerb, $myNoun = $MyInvocationName -split '-' - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - $IsGet = $myVerb -eq "Get" - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +} - if ( - $IsGet -or - $NoVerb - ) { - $inputsOfKind = - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - if ($NonNameParameters -and -not $IsGet) { - $paramCopy = [Ordered]@{} + $PSBoundParameters - if ($paramCopy.Name) { $paramCopy.Remove('Name') } - $inputsOfKind | & $myScriptBlock @paramCopy - } else { - $inputsOfKind - } - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName +} + + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSScene { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] +[Alias('obs.powershell.websocket.CreateScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } } - - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($AudioSource) { - "$($AudioSource)-Waveform" - } else { - "Waveform" + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } + } } - if ($myParameterData.color_base) { - $myParameterData.color_base = $myParameterData.color_base | ToOBSColor + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($myParameterData.color_crest) { - $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +} - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } +} - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - } - - } -} #.ExternalHelp obs-powershell-Help.xml -function Set-OBSWindowSource { - - - [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] - param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] - [string] - $WindowTitle, +function Add-OBSSceneCollection { - # The number of the capture method. By default, automatic (0). - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("method")] - [int] - $CaptureMethod, - # The capture priority. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet('ExactMatch','SameType','SameExecutable')] - [string] - $CapturePriority, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] +[Alias('obs.powershell.websocket.CreateSceneCollection')] +param( - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cursor")] - [switch] - $CaptureCursor, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, will capture the client area. - # This will be set by default. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("client_area")] - [switch] - $ClientArea, - # If set, will force SDR. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("force_sdr")] - [switch] - $ForceSDR, +process { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' - - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - - } - begin { - $InputKind = "window_capture" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - - if (-not $myParameters["WindowTitle"]) { - while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { - $_ = $_.Parent - } - if ($_.MainWindowTitle) { - $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # Window capture is a bit of a tricky one. - # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. - # and for that, an input needs to exist. - if (-not $myParameters["Name"]) { - if ($myParameters["WindowTitle"]) { - $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] - } - else { - $Name = "WindowCapture" - } - } +} - - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSSceneItem { - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } - if ($null -ne $CaptureMethod) { - $myParameterData["method"] = $CaptureMethod - } - if ($CapturePriority -eq 'ExactMatch') { - $myParameterData["priority"] = 1 - } - elseif ($CapturePriority -eq 'SameType') { - $myParameterData["priority"] = 0 - } - elseif ($CapturePriority -eq 'SameExecutable') { - $myParameterData["priority"] = 2 - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] +[Alias('obs.powershell.websocket.CreateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Add-OBSSceneSource')] +param( - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "window_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window - foreach ($windowInfo in $possibleWindows) { - if (-not $WindowTitle) { continue } - if ( - ($windowInfo.itemName -eq $WindowTitle) -or - ($windowInfo.ItemValue -eq $WindowTitle) -or - ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or - ($windowInfo.ItemValue -like "*$WindowTitle*") -or - ($windowInfo.ItemName -like "*$WindowTitle*") - ) { - $myParameterData["window"] = $windowInfo.itemValue - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name - } + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - - } + } + + +} + #.ExternalHelp obs-powershell-Help.xml -function Get-OBS3dSwapTransitionShader { +function Add-OBSSourceFilter { -[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] +[Alias('obs.powershell.websocket.CreateSourceFilter')] param( -# Set the image_a of OBS3dSwapTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBS3dSwapTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBS3dSwapTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBS3dSwapTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the reflection of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('reflection')] -[Single] -$Reflection, -# Set the perspective of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('perspective')] -[Single] -$Perspective, -# Set the depth of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('depth')] -[Single] -$Depth, -# Set the background_color of OBS3dSwapTransitionShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[String] -$BackgroundColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = '3d_swap_transition' -$ShaderNoun = 'OBS3dSwapTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MlXGzf - -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -uniform float reflection< - string label = "Reflection (0.4)"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.4; -uniform float perspective< - string label = "Perspective (0.2)"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = .2; -uniform float depth< - string label = "Depth (3.0)"; - string widget_type = "slider"; - float minimum = 1.00; - float maximum = 10.00; - float step = 0.1; -> = 3.; - -#ifndef OPENGL -#define lessThan(a,b) (a < b) -#endif -uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; - -bool inBounds (float2 p) { - return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); -} - -float2 project (float2 p) { - return p * float2(1.0, -1.2) + float2(0.0, 2.22); -} - -float4 bgColor (float2 p, float2 pfr, float2 pto) { - float4 c = background_color; - pfr = project(pfr); - if (inBounds(pfr)) { - c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); - } - pto = project(pto); - if (inBounds(pto)) { - c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); - } - return c; -} - -float4 mainImage(VertData v_in) : TARGET { - float2 p = v_in.uv; - float2 pfr = float2(-1.,-1.); - float2 pto = float2(-1.,-1.); - - float progress = transition_time; - float size = lerp(1.0, depth, progress); - float persp = perspective * progress; - pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); - - size = lerp(1.0, depth, 1.-progress); - persp = perspective * (1.-progress); - pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); - - bool fromOver = progress < 0.5; - float4 rgba = background_color; - if (fromOver) { - if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); - } - else if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else { - rgba = bgColor(p, pfr, pto); - } - } - else { - if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); - } - else { - rgba = bgColor(p, pfr, pto); - } - } - if (convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -4715,147 +3597,230 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAddShader { +function Copy-OBSSceneItem { -[Alias('Set-OBSAddShader','Add-OBSAddShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] +[Alias('obs.powershell.websocket.DuplicateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the other_image of OBSAddShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] -[String] -$OtherImage, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneName')] +[string] +$DestinationSceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] +[string] +$DestinationSceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Add' -$ShaderNoun = 'OBSAddShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return clamp(base + other, 0.0, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } +} - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentPreviewScene { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -4864,182 +3829,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaBorderShader { +function Get-OBSCurrentProgramScene { -[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] +[Alias('obs.powershell.websocket.GetCurrentProgramScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the border_color of OBSAlphaBorderShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_thickness of OBSAlphaBorderShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the alpha_cut_off of OBSAlphaBorderShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'alpha_border' -$ShaderNoun = 'OBSAlphaBorderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 border_color< - string label = "Border color"; -> = {0.0,0.0,0.0,1.0}; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 pix = image.Sample(textureSampler, v_in.uv); - if (pix.a > alpha_cut_off) - return pix; - [loop] for(int x = -border_thickness;x alpha_cut_off) - return border_color; - } - } - } - return pix; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5048,300 +3932,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaGamingBentCameraShader { +function Get-OBSCurrentSceneTransition { -[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the left_side_width of OBSAlphaGamingBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSAlphaGamingBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSAlphaGamingBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSAlphaGamingBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSAlphaGamingBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] -[Single] -$RightSideShadow, -# Set the right_flip_width of OBSAlphaGamingBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] -[Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] -[Single] -$RightFlipShadow, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'alpha-gaming-bent-camera' -$ShaderNoun = 'OBSAlphaGamingBentCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; -uniform float right_side_width< - string label = "Right side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; - } - - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; - } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5350,270 +4035,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedPathShader { +function Get-OBSCurrentSceneTransitionCursor { -[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedPathShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedPathShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedPathShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedPathShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedPathShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the speed_percent of OBSAnimatedPathShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the path_map of OBSAnimatedPathShader -[Alias('path_map')] -[ComponentModel.DefaultBindingProperty('path_map')] -[String] -$PathMap, -# Set the reverse of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'animated_path' -$ShaderNoun = 'OBSAnimatedPathShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; - -uniform int speed_percent = 100; -uniform texture2d path_map; -uniform bool reverse = false; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -float4 convert_pmalpha(float4 c) -{ - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - float3 pos = v_in.pos.xyz; - float3 current_pos; - float speed = speed_percent * 0.01; - //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float t = 1.0 + sin(elapsed_time * speed) ; - // combine luma texture and user defined shine color - float luma = path_map.Sample(textureSampler, v_in.uv).x; - if (reverse) - { - luma = 1.0 - luma; - } - - float time = lerp(0.0f, 1.0f , t - 1.0); - - // set current position in time - current_pos.x = 0; - current_pos.y = 0; - - - float2 offset = uv_offset; - if (speed == 0.0f) - { - offset.x = 0.0f; - offset.y = 0.0f; - } - else - { - offset.x = uv_offset.x + time * luma; - offset.y = uv_offset.y + time * luma; - } - - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} - -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5622,403 +4138,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedTextureShader { +function Get-OBSGroup { -[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] +[Alias('obs.powershell.websocket.GetGroupList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSAnimatedTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Image of OBSAnimatedTextureShader -[Alias('Animation_Image')] -[ComponentModel.DefaultBindingProperty('Animation_Image')] -[String] -$AnimationImage, -# Set the Colorization_Image of OBSAnimatedTextureShader -[Alias('Colorization_Image')] -[ComponentModel.DefaultBindingProperty('Colorization_Image')] -[String] -$ColorizationImage, -# Set the reverse of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the bounce of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('bounce')] -[Management.Automation.SwitchParameter] -$Bounce, -# Set the center_animation of OBSAnimatedTextureShader -[Alias('center_animation')] -[ComponentModel.DefaultBindingProperty('center_animation')] -[Management.Automation.SwitchParameter] -$CenterAnimation, -# Set the polar_animation of OBSAnimatedTextureShader -[Alias('polar_animation')] -[ComponentModel.DefaultBindingProperty('polar_animation')] -[Management.Automation.SwitchParameter] -$PolarAnimation, -# Set the polar_angle of OBSAnimatedTextureShader -[Alias('polar_angle')] -[ComponentModel.DefaultBindingProperty('polar_angle')] -[Single] -$PolarAngle, -# Set the polar_height of OBSAnimatedTextureShader -[Alias('polar_height')] -[ComponentModel.DefaultBindingProperty('polar_height')] -[Single] -$PolarHeight, -# Set the speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] -[Single] -$SpeedHorizontalPercent, -# Set the speed_vertical_percent of OBSAnimatedTextureShader -[Alias('speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] -[Single] -$SpeedVerticalPercent, -# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('tint_speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] -[Single] -$TintSpeedHorizontalPercent, -# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader -[Alias('tint_speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] -[Single] -$TintSpeedVerticalPercent, -# Set the Alpha of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('Alpha')] -[Single] -$Alpha, -# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader -[Alias('Use_Animation_Image_Color')] -[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] -[Management.Automation.SwitchParameter] -$UseAnimationImageColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'animated_texture' -$ShaderNoun = 'OBSAnimatedTextureShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 -// Animates a texture with polar sizing and color options -// for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes; - -uniform texture2d Animation_Image; -uniform texture2d Colorization_Image; -uniform bool reverse = false; -uniform bool bounce = false; -uniform bool center_animation = true; -uniform bool polar_animation = true; -uniform float polar_angle = 90.0; -uniform float polar_height = 1.0; -uniform float speed_horizontal_percent = 50; -uniform float speed_vertical_percent = 5; -uniform float tint_speed_horizontal_percent = 50; -uniform float tint_speed_vertical_percent = 5; -uniform float Alpha = 1.0; -uniform bool Use_Animation_Image_Color = true; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -float4 convert_pmalpha(float4 color) -{ - float4 ret = color; - if (color.a >= 0.001) - ret.xyz /= color.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} - -float2 time(float2 speed_dir) -{ - float PI = 3.1415926535897932384626433832795; //acos(-1); - float2 t = (elapsed_time * speed_dir) ; - if (bounce) - { - // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen - t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; - t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; - t *= -0.5; - } - if (reverse) - t = t * -1; - return t; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -VertData mainTransform(VertData v_in) -{ - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - VertData vert_out; - //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float2 offset = uv_offset; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if (center_animation) - { - vert_out.uv = v_in.uv - 0.5f; - } - else - { - offset += time(speed_dir); - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return vert_out; } -float4 mainImage(VertData v_in) : TARGET -{ - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); - float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); - - //compensate for background vertex shader values - float2 background_offset = float2(-.5,-.5); - if (!center_animation) - background_offset = time(speed_dir); - float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); +} - // Convert our texture coordinates to polar form: - if (polar_animation) { - - float2 polar = float2( - atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle - log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius - ); + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGroupSceneItem { - // Check how much our texture sampling point changes between - // neighbouring pixels to the sides (ddx) and above/below (ddy) - ///float4 gradient = float4(ddx(polar), ddy(polar)); - // If our angle wraps around between adjacent samples, - // discard one full rotation from its value and keep the fraction. - ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] +[Alias('obs.powershell.websocket.GetGroupSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - float2 tintUVs = polar * 4; - tintUVs += time(tint_speed_dir); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - // Apply texture scale - polar *= 4; - // Scroll the texture over time. - polar += time(speed_dir); - float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); - +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); - float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); - //animation.a = clamp((intensity),0.0,1.0); - if (Use_Animation_Image_Color) - { - animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - else - { - animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - //if (keyAmount > 0.5f) - rgba = lerp(rgba, animation, animation.a * Alpha); - } - return rgba; -} +process { -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6027,256 +4354,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAsciiShader { +function Get-OBSHotkey { -[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] +[Alias('obs.powershell.websocket.GetHotkeyList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the scale of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('scale')] -[Int32] -$Scale, -# Set the base_color of OBSAsciiShader -[Alias('base_color')] -[ComponentModel.DefaultBindingProperty('base_color')] -[String] -$BaseColor, -# Set the monochrome of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('monochrome')] -[Management.Automation.SwitchParameter] -$Monochrome, -# Set the character_set of OBSAsciiShader -[Alias('character_set')] -[ComponentModel.DefaultBindingProperty('character_set')] -[Int32] -$CharacterSet, -# Set the note of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('note')] -[String] -$Note, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'ascii' -$ShaderNoun = 'OBSAsciiShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 -// https://github.com/Oncorporation/obs-shaderfilter -// Based on the following shaders: -// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 -// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 - -// Modifications of original shaders include: -// - Porting from GLSL to HLSL -// - Combining characters sets from both source shaders -// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set -// -// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ -// converts a bitmap into int then decodes it to look like text - -uniform int scale< - string label = "Scale"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; // Size of characters -uniform float4 base_color< - string label = "Base color"; -> = {0.0,1.0,0.0,1.0}; // Monochrome base color -uniform bool monochrome< - string label = "Monochrome"; -> = false; -uniform int character_set< - string label = "Character set"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Large set of non-letters"; - int option_1_value = 1; - string option_1_label = "Small set of capital letters"; -> = 0; -uniform string note< - string widget_type = "info"; -> = "Base color is used as monochrome base color."; - -float character(int n, float2 p) -{ - p = floor(p*float2(4.0, 4.0) + 2.5); - if (clamp(p.x, 0.0, 4.0) == p.x) - { - if (clamp(p.y, 0.0, 4.0) == p.y) - { - int a = int(round(p.x) + 5.0 * round(p.y)); - if (((n >> a) & 1) == 1) return 1.0; - } - } - return 0.0; -} - -float2 mod(float2 x, float2 y) -{ - return x - y * floor(x/y); -} - -float4 mainImage( VertData v_in ) : TARGET -{ - float2 iResolution = uv_size*uv_scale; - float2 pix = v_in.pos.xy; - float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); - - float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; - - int n; - int charset = clamp(character_set, 0, 1); - - if (charset==0) - { - if (gray <= 0.2) n = 4096; // . - if (gray > 0.2) n = 65600; // : - if (gray > 0.3) n = 332772; // * - if (gray > 0.4) n = 15255086; // o - if (gray > 0.5) n = 23385164; // & - if (gray > 0.6) n = 15252014; // 8 - if (gray > 0.7) n = 13199452; // @ - if (gray > 0.8) n = 11512810; // # - } - else if (charset==1) - { - if (gray <= 0.1) n = 0; - if (gray > 0.1) n = 9616687; // R - if (gray > 0.3) n = 32012382; // S - if (gray > 0.5) n = 16303663; // D - if (gray > 0.7) n = 15255086; // O - if (gray > 0.8) n = 16301615; // B - } - float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); - - if (monochrome) - { - c.rgb = base_color.rgb; - } - c = c*character(n, p); - - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6285,272 +4457,219 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAspectRatioShader { +function Get-OBSInput { -[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] +[Alias('obs.powershell.websocket.GetInputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAspectRatioShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAspectRatioShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAspectRatioShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAspectRatioShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAspectRatioShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSAspectRatioShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the borderColor of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('borderColor')] -[String] -$BorderColor, -# Set the notes of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'aspect_ratio' -$ShaderNoun = 'OBSAspectRatioShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -// variables -uniform float4 borderColor = {0,0,0,0}; -float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; -uniform string notes; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 hw = uv_scale; - // determine the game window''s current aspect ratio - float windowaspect = hw.x / hw.y; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // current viewport height should be scaled by this amount - float scaleheight = windowaspect / targetaspect; +} - // if scaled height is less than current height, add letterbox - if (scaleheight < 1.0f) - { - Rect rect = camera.rect; +} - rect.width = 1.0f; - rect.height = scaleheight; - rect.x = 0; - rect.y = (1.0f - scaleheight) / 2.0f; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioBalance { - camera.rect = rect; - } - else // add pillarbox - { - float scalewidth = 1.0f / scaleheight; - Rect rect = camera.rect; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] +[Alias('obs.powershell.websocket.GetInputAudioBalance')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - rect.width = scalewidth; - rect.height = 1.0f; - rect.x = (1.0f - scalewidth) / 2.0f; - rect.y = 0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - camera.rect = rect; - } - return vert_out; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float4 mainImage(VertData v_in) : TARGET -{ - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) - { - return borderColor; - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} +process { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6559,315 +4678,224 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBackgroundRemovalShader { +function Get-OBSInputAudioMonitorType { -[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSBackgroundRemovalShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSBackgroundRemovalShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSBackgroundRemovalShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSBackgroundRemovalShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSBackgroundRemovalShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSBackgroundRemovalShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the target of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('target')] -[String] -$Target, -# Set the color of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the opacity of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, -# Set the invert of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the Convert_709to601 of OBSBackgroundRemovalShader -[Alias('Convert_709to601')] -[ComponentModel.DefaultBindingProperty('Convert_709to601')] -[Management.Automation.SwitchParameter] -$Convert709to601, -# Set the Convert_601to709 of OBSBackgroundRemovalShader -[Alias('Convert_601to709')] -[ComponentModel.DefaultBindingProperty('Convert_601to709')] -[Management.Automation.SwitchParameter] -$Convert601to709, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'background_removal' -$ShaderNoun = 'OBSBackgroundRemovalShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; -uniform texture2d target; -uniform float4 color; -uniform float opacity = 15.0; -uniform bool invert; -uniform bool Convert_709to601; -uniform bool Convert_601to709; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float dot(float3 a,float3 b){ - return a.x*b.x+a.y*b.y+a.z*b.z; } -//BT.601 to BT.709 -// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input -// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. -float4 Convert601to709(float4 rgba) -{ - float3 s1 = rgba.rgb; - s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace - return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace -} -//BT.709 to BT.601 -float4 Convert709to601(float4 rgba) -{ - float3 s1 = rgba.rgb; - s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); - return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; -} +} -VertDataOut VSDefault(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); - vert_out.uv = v_in.uv; - vert_out.uv2 = v_in.uv * uv_scale + uv_offset; - return vert_out; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioSyncOffset { -float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET -{ - float Tolerance = opacity * 0.01; - float4 rgba = image.Sample(textureSampler, v_in.uv); - float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; - if (invert){ - targetRGB.rgb = 1.0 - targetRGB.rgb; - } - if (Convert_709to601) - { - rgba.rgb = Convert709to601(rgba).rgb; - targetRGB.rgb = Convert709to601(targetRGB).rgb; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - if (Convert_601to709) - { - rgba.rgb = Convert601to709(rgba).rgb; - targetRGB.rbg = Convert601to709(targetRGB).rgb; - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - float4 shadowRGB = targetRGB * targetRGB; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if ((abs(targetRGB.r - rgba.r) <= Tolerance && - abs(targetRGB.g - rgba.g) <= Tolerance && - abs(targetRGB.b - rgba.b) <= Tolerance) - || (abs(shadowRGB.r - rgba.r) <= Tolerance && - abs(shadowRGB.g - rgba.g) <= Tolerance && - abs(shadowRGB.b - rgba.b) <= Tolerance)) - { - rgba.rgba = float4(0,0,0,0); - } - return rgba; -} -technique Draw -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSColorMaskRGBA(v_in); - } -} +process { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6876,255 +4904,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlendOpacityShader { +function Get-OBSInputAudioTracks { -[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] +[Alias('obs.powershell.websocket.GetInputAudioTracks')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Vertical of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the Rotational of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Rotational')] -[Management.Automation.SwitchParameter] -$Rotational, -# Set the Rotation_Offset of OBSBlendOpacityShader -[Alias('Rotation_Offset')] -[ComponentModel.DefaultBindingProperty('Rotation_Offset')] -[Single] -$RotationOffset, -# Set the Opacity_Start_Percent of OBSBlendOpacityShader -[Alias('Opacity_Start_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] -[Single] -$OpacityStartPercent, -# Set the Opacity_End_Percent of OBSBlendOpacityShader -[Alias('Opacity_End_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] -[Single] -$OpacityEndPercent, -# Set the Spread of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Spread')] -[Single] -$Spread, -# Set the Speed of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Notes of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'blend_opacity' -$ShaderNoun = 'OBSBlendOpacityShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 14, 2022 -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.01; -> = 0.0; -uniform float Opacity_Start_Percent< - string label = "Opacity Start Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 0.0; -uniform float Opacity_End_Percent< - string label = "Opacity End Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform float Spread< - string label = "Spread"; - string widget_type = "slider"; - float minimum = 0.25; - float maximum = 10.0; - float step = 0.01; -> = 0.5; -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.0; -uniform bool Apply_To_Alpha_Layer = true; -uniform string Notes< - string widget_type = "info"; -> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 point_color = image.Sample(textureSampler, v_in.uv); - float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; - float4 gray = float4(luminance,luminance,luminance, 1); - - float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); - - if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) - { - //set opacity and direction - float opacity = (-1 * lPos.x) * 0.5; - - if (Rotational && (Vertical == false)) - { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; - } - - if (Vertical && (Rotational == false)) - { - opacity = (-1 * lPos.y) * 0.5; - } - - opacity += time; - opacity = frac(opacity); - point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); - } - return point_color; -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7133,152 +5017,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlinkShader { +function Get-OBSInputDefaultSettings { -[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] +[Alias('obs.powershell.websocket.GetInputDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the speed of OBSBlinkShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'blink' -$ShaderNoun = 'OBSBlinkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - return float4(color.r, color.g, color.b, color.a * (1 + sin(t)) / 2); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7287,218 +5125,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBloomShader { +function Get-OBSInputKind { -[Alias('Set-OBSBloomShader','Add-OBSBloomShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] +[Alias('obs.powershell.websocket.GetInputKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Angle_Steps of OBSBloomShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSBloomShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSBloomShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the notes of OBSBloomShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('unversioned')] +[switch] +$Unversioned, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'bloom' -$ShaderNoun = 'OBSBloomShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Bloom shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; // -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; // -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 2.0; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - int radiusSteps = clamp(Radius_Steps, 0, 20); - int angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - float minRadius = (0.0 * uv_pixel_interval.y); - float maxRadius = (10.0 * uv_pixel_interval.y); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 outputPixel = c0; - float4 accumulatedColor = float4(0,0,0,0); - - int totalSteps = radiusSteps * angleSteps; - float angleDelta = (2.0 * PI) / float(angleSteps); - float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); - - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + float(radiusStep) * radiusDelta; - - for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { - float2 currentCoord; - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor =image.Sample(textureSampler, currentCoord); - float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); - - accumulatedColor += currentFraction * currentColor / float(totalSteps); - - } - } - - outputPixel += accumulatedColor * ampFactor; - - return outputPixel; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7507,151 +5233,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBorderShader { +function Get-OBSInputMute { -[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] +[Alias('obs.powershell.websocket.GetInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the borderColor of OBSBorderShader -[ComponentModel.DefaultBindingProperty('borderColor')] -[String] -$BorderColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'border' -$ShaderNoun = 'OBSBorderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 borderColor; -float4 mainImage(VertData v_in) : TARGET -{ - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) - { - return borderColor; - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7660,237 +5346,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBoxBlurShader { +function Get-OBSInputPropertiesListPropertyItems { -[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] +[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Strength of OBSBoxBlurShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSBoxBlurShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSBoxBlurShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSBoxBlurShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSBoxBlurShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'box-blur' -$ShaderNoun = 'OBSBoxBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - float transparent = 0.0; - int count = 1; - float samples = 0.0; - float4 c = float4(0.0, 0.0, 0.0, 0.0); - float Steps = float(Strength); - - [loop] for (int i = -Strength; i <= Strength; i++) { - [loop] for (int k = -Strength; k <= Strength; k++) { - float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); - transparent += sc.a; - count++; - c += sc * sc.a; - samples += sc.a; - } - } - if(samples > 0.0) - c /= samples; - c.a = transparent / float(count); - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7899,227 +5464,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBulgePinchShader { +function Get-OBSInputSettings { -[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] +[Alias('obs.powershell.websocket.GetInputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the radius of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the magnitude of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('magnitude')] -[Single] -$Magnitude, -# Set the center_x of OBSBulgePinchShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSBulgePinchShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the animate of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the notes of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'BulgePinch' -$ShaderNoun = 'OBSBulgePinchShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.0; -uniform float magnitude< - string label = "Magnitude"; - string widget_type = "slider"; - float minimum = -1.3333; - float maximum = 1.3333; - float step = 0.01; -> = 0.0; -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.01; -> = 0.25; -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.01; -> = 0.25; -uniform bool animate = false; - -uniform string notes< - string widget_type = "info"; -> = "Distorts the screen, expanding or drawing in pixels around a point." -float4 mainImage(VertData v_in) : TARGET -{ - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - v_out.uv = 1. * v_in.uv - center; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - center.x /= ar; - v_out.uv.x /= ar; - - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); - float percent = dist/radius; - if(anim_mag > 0) - v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); - else - v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); - - v_out.uv += (2 * center); - v_out.uv.x *= ar; - - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8128,343 +5577,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBurnShader { +function Get-OBSInputVolume { -[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] +[Alias('obs.powershell.websocket.GetInputVolume')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Burn_Gradient of OBSBurnShader -[Alias('Burn_Gradient')] -[ComponentModel.DefaultBindingProperty('Burn_Gradient')] -[String] -$BurnGradient, -# Set the Speed of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Gradient_Adjust of OBSBurnShader -[Alias('Gradient_Adjust')] -[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] -[Single] -$GradientAdjust, -# Set the Dissolve_Value of OBSBurnShader -[Alias('Dissolve_Value')] -[ComponentModel.DefaultBindingProperty('Dissolve_Value')] -[Single] -$DissolveValue, -# Set the Animated of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the Apply_to_Channel of OBSBurnShader -[Alias('Apply_to_Channel')] -[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the Apply_Smoke of OBSBurnShader -[Alias('Apply_Smoke')] -[ComponentModel.DefaultBindingProperty('Apply_Smoke')] -[Management.Automation.SwitchParameter] -$ApplySmoke, -# Set the Smoke_Horizonal_Speed of OBSBurnShader -[Alias('Smoke_Horizonal_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] -[Single] -$SmokeHorizonalSpeed, -# Set the Smoke_Vertical_Speed of OBSBurnShader -[Alias('Smoke_Vertical_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] -[Single] -$SmokeVerticalSpeed, -# Set the Iterations of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Iterations')] -[Int32] -$Iterations, -# Set the Notes of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'burn' -$ShaderNoun = 'OBSBurnShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Exeldro February 17, 2022 -float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} -float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} -float4 permute(float4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} -float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} -float2 fade(float2 t) { - return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); -} -float dot(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} -// Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} -// Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } -uniform texture2d Burn_Gradient = "burngradient.png"; -uniform float Speed = 0.33; -uniform float Gradient_Adjust = 0.85; -uniform float Dissolve_Value = 1.43; -uniform bool Animated; -uniform bool Apply_to_Channel; -uniform bool Apply_Smoke = true; -uniform float Smoke_Horizonal_Speed = 0.3; -uniform float Smoke_Vertical_Speed = 0.17; -uniform int Iterations = 4; -uniform string Notes< - string widget_type = "info"; -> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - float4 smoke = float4(1.0,1.0,1.0,1.0); - float4 result = smoke; - float t = elapsed_time * Speed; - float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay - float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); - //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); +} - float perlin = 0.5; - float smoke_perlin = 0; - float scale = 1; - float w = 0.5; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSLastReplayBufferReplay { - for (int i = 0; i < Iterations; i++) { - //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; - float2 coord = (v_in.uv + t * (dir * .1)) * scale; - float2 period = scale * dir; - perlin += pnoise(coord, period) * w; - if (Apply_Smoke) - smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; - scale *= 2.0; - w *= 0.5; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] +[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); - if (!Animated) - cycle = 1; - float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); - float overOne = saturate(d * Gradient_Adjust); - float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); - if (Apply_to_Channel) { - result = c * burn; - } - else { - result = float4(perlin, perlin, perlin, 1.0) * burn; - } +process { - if (smoke_perlin > 0) { - smoke *= smoke_perlin; - if (result.a <= 0.04) - result = float4(smoke.rgb, smoke_perlin); - result += smoke; - } - return result; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8473,286 +5793,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCartoonShader { +function Get-OBSMediaInputStatus { -[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] +[Alias('obs.powershell.websocket.GetMediaInputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSCartoonShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSCartoonShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSCartoonShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSCartoonShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSCartoonShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSCartoonShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the hue_steps of OBSCartoonShader -[Alias('hue_steps')] -[ComponentModel.DefaultBindingProperty('hue_steps')] -[Int32] -$HueSteps, -# Set the value_steps of OBSCartoonShader -[Alias('value_steps')] -[ComponentModel.DefaultBindingProperty('value_steps')] -[Int32] -$ValueSteps, -# Set the Apply_To_Alpha_Layer of OBSCartoonShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cartoon' -$ShaderNoun = 'OBSCartoonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "5/2 seems reasonable"; - -uniform int hue_steps = 5; -uniform int value_steps = 2; -uniform bool Apply_To_Alpha_Layer = true; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertDataOut VSDefault(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv; - return vert_out; -} - -float3 rgb2hsv(float3 c) -{ - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -float3 hsv2rgb(float3 c) -{ - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} - -float fit(float v, int factor) -{ - return round(v * factor) / factor; -} - -float hue_wrap(float h) -{ - return fmod(h + 1, 2) - 1; - if(h > 1) - return h - 2; - if(h < -1) - return h + 2; - return h; -} - -float4 PassThrough(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) - { - float3 hsv = rgb2hsv(rgba.rgb); - hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); - //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); - rgba = float4(hsv2rgb(hsv), rgba.a); - //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); - } - return rgba; -} -technique Draw -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PassThrough(v_in); - } -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8761,217 +5906,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCellShadedShader { +function Get-OBSMonitor { -[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] +[Alias('obs.powershell.websocket.GetMonitorList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Angle_Steps of OBSCellShadedShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSCellShadedShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the notes of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cell_shaded' -$ShaderNoun = 'OBSCellShadedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 2.0; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (3 * uv_pixel_interval.y); - float maxRadius = (24 * uv_pixel_interval.y); - - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); - - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; - - for (float angle=0; angle <(2*PI); angle += angleDelta) { - float2 currentCoord; - - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps; - - } - } - accumulatedColor *= ampFactor; - return c0 - accumulatedColor; // Cell shaded style -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8980,376 +6009,209 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaticAberrationShader { +function Get-OBSOutput { -[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] +[Alias('obs.powershell.websocket.GetOutputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the power of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the gamma of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('gamma')] -[Single] -$Gamma, -# Set the num_iter of OBSChromaticAberrationShader -[Alias('num_iter')] -[ComponentModel.DefaultBindingProperty('num_iter')] -[Int32] -$NumIter, -# Set the distort_radial of OBSChromaticAberrationShader -[Alias('distort_radial')] -[ComponentModel.DefaultBindingProperty('distort_radial')] -[Management.Automation.SwitchParameter] -$DistortRadial, -# Set the distort_barrel of OBSChromaticAberrationShader -[Alias('distort_barrel')] -[ComponentModel.DefaultBindingProperty('distort_barrel')] -[Management.Automation.SwitchParameter] -$DistortBarrel, -# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader -[Alias('offset_spectrum_ycgco')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] -[Management.Automation.SwitchParameter] -$OffsetSpectrumYcgco, -# Set the offset_spectrum_yuv of OBSChromaticAberrationShader -[Alias('offset_spectrum_yuv')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] -[Management.Automation.SwitchParameter] -$OffsetSpectrumYuv, -# Set the use_random of OBSChromaticAberrationShader -[Alias('use_random')] -[ComponentModel.DefaultBindingProperty('use_random')] -[Management.Automation.SwitchParameter] -$UseRandom, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'chromatic-aberration' -$ShaderNoun = 'OBSChromaticAberrationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/XssGz8 -//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.01; -uniform float gamma< - string label = "Gamma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3.0; - float step = 0.01; -> = 2.2; -uniform int num_iter< - string label = "Number iterations"; - string widget_type = "slider"; - int minimum = 3; - int maximum = 25; - int step = 1; -> = 7; -uniform bool distort_radial = false; -uniform bool distort_barrel = false; -uniform bool offset_spectrum_ycgco = false; -uniform bool offset_spectrum_yuv = false; -uniform bool use_random = true; -float2 remap( float2 t, float2 a, float2 b ) { - return clamp( (t - a) / (b - a), 0.0, 1.0 ); -} -float3 spectrum_offset_rgb( float t ) -{ - float t0 = 3.0 * t - 1.5; - float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); - return ret; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float3 lin2srgb( float3 c ) -{ - return pow( c, float3(gamma, gamma, gamma) ); -} -float3 srgb2lin( float3 c ) -{ - return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 yCgCo2rgb(float3 ycc) -{ - float R = ycc.x - ycc.y + ycc.z; - float G = ycc.x + ycc.y; - float B = ycc.x - ycc.y - ycc.z; - return float3(R,G,B); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float3 spectrum_offset_ycgco( float t ) -{ - //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink - //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple - float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange - //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue - return yCgCo2rgb( ygo ); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 yuv2rgb( float3 yuv ) -{ - float3 rgb; - rgb.r = yuv.x + yuv.z * 1.13983; - rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); - rgb.b = yuv.x + yuv.y * 2.03211; - return rgb; } -float2 radialdistort(float2 coord, float2 amt) -{ - float2 cc = coord - 0.5; - return coord + 2.0 * cc * amt; -} -float2 barrelDistortion( float2 p, float2 amt ) -{ - p = 2.0 * p - 1.0; +} - /* - const float maxBarrelPower = 5.0; - //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper - float theta = atan(p.y, p.x); - float2 radius = float2( length(p) ); - radius = pow(radius, 1.0 + maxBarrelPower * amt); - p.x = radius.x * cos(theta); - p.y = radius.y * sin(theta); + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSOutputSettings { - /*/ - // much faster version - //const float maxBarrelPower = 5.0; - //float radius = length(p); - float maxBarrelPower = sqrt(5.0); - float radius = dot(p,p); //faster but doesn''t match above accurately - p *= pow(float2(radius, radius), maxBarrelPower * amt); - /* */ - return p * 0.5 + 0.5; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] +[Alias('obs.powershell.websocket.GetOutputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float2 brownConradyDistortion(float2 uv, float dist) -{ - uv = uv * 2.0 - 1.0; - // positive values of K1 give barrel distortion, negative give pincushion - float barrelDistortion1 = 0.1 * dist; // K1 in text books - float barrelDistortion2 = -0.025 * dist; // K2 in text books +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float r2 = dot(uv,uv); - uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; - //uv *= 1.0 + barrelDistortion1 * r2; - - // tangential distortion (due to off center lens elements) - // is not modeled in this function, but if it was, the terms would go here - return uv * 0.5 + 0.5; -} -float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) -{ - float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); - //float2 dist = mix( min_distort, max_distort, t ); - if (distort_radial) - return radialdistort( uv, 2.0 * dist ); - - if(distort_barrel) - return barrelDistortion( uv, 1.75 * dist ); //distortion at center - return brownConradyDistortion( uv, 75.0 * dist.x ); -} +process { -// ==== -float3 spectrum_offset_yuv( float t ) -{ - //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green - //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green - float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange - //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue - return yuv2rgb( yuv ); -} - -float3 spectrum_offset( float t ) -{ - if(offset_spectrum_ycgco) - return spectrum_offset_ycgco( t ); - if(offset_spectrum_yuv) - return spectrum_offset_yuv( t ); - return spectrum_offset_rgb( t ); - //return srgb2lin( spectrum_offset_rgb( t ) ); - //return lin2srgb( spectrum_offset_rgb( t ) ); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 max_distort = float2(power, power); - float2 min_distort = 0.5 * max_distort; - - float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); - - float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); - - //debug oversiz - //float2 distuv = distort( uv, 1.0, max_distort ); - //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) - //{ - // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; - //} - - - float stepsiz = 1.0 / (float(num_iter)-1.0); - float rnd = 0.0; - if(use_random) - rnd = rand_f; - - float t = rnd * stepsiz; - - float3 sumcol = float3(0.0, 0.0, 0.0); - float3 sumw = float3(0.0, 0.0, 0.0); - float colA = 0.0; - - for ( int i=0; iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -9358,205 +6220,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaUVDistortionShader { +function Get-OBSOutputStatus { -[Alias('Set-OBSChromaUVDistortionShader','Add-OBSChromaUVDistortionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] +[Alias('obs.powershell.websocket.GetOutputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the distortion of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('distortion')] -[Single] -$Distortion, -# Set the amplitude of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the chroma of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('chroma')] -[Single] -$Chroma, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Chroma+UV-Distortion' -$ShaderNoun = 'OBSChromaUVDistortionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/WsdyRN - -//Higher values = less distortion -uniform float distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 5.0; - float maximum = 1000.0; - float step = 0.01; -> = 75.; -//Higher values = tighter distortion -uniform float amplitude< - string label = "Amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 10.; -//Higher values = more color distortion -uniform float chroma< - string label = "Chroma"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.01; -> = .5; - -float2 zoomUv(float2 uv, float zoom) { - float2 uv1 = uv; - uv1 += .5; - uv1 += zoom/2.-1.; - uv1 /= zoom; - return uv1; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 uvt = v_in.uv; - - float2 uvtR = uvt; - float2 uvtG = uvt; - float2 uvtB = uvt; - - //Uncomment the following line to get varying chroma distortion - //chroma = sin(elapsed_time)/2.+.5; - - uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); - uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); - uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); - - float2 uvR = zoomUv(uvtR, 1.1); - float2 uvG = zoomUv(uvtG, 1.1); - float2 uvB = zoomUv(uvtB, 1.1); - - float4 colR = image.Sample(textureSampler, uvR); - float4 colG = image.Sample(textureSampler, uvG); - float4 colB = image.Sample(textureSampler, uvB); - return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -9565,238 +6328,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCircleMaskFilterShader { +function Get-OBSPersistentData { -[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] +[Alias('obs.powershell.websocket.GetPersistentData')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Radius of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Radius')] -[Single] -$Radius, -# Set the Circle_Offset_X of OBSCircleMaskFilterShader -[Alias('Circle_Offset_X')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] -[Int32] -$CircleOffsetX, -# Set the Circle_Offset_Y of OBSCircleMaskFilterShader -[Alias('Circle_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] -[Int32] -$CircleOffsetY, -# Set the Source_Offset_X of OBSCircleMaskFilterShader -[Alias('Source_Offset_X')] -[ComponentModel.DefaultBindingProperty('Source_Offset_X')] -[Int32] -$SourceOffsetX, -# Set the Source_Offset_Y of OBSCircleMaskFilterShader -[Alias('Source_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] -[Int32] -$SourceOffsetY, -# Set the Antialiasing of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Antialiasing')] -[Management.Automation.SwitchParameter] -$Antialiasing, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'circle-mask-filter' -$ShaderNoun = 'OBSCircleMaskFilterShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Circle Mask Filter version 1.01, for OBS Shaderfilter -// Copyright 2022 by SkeletonBow -// Twitter: -// Twitch: -// License: GNU GPLv2 -// -// Changelog: -// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. -// 1.0 - Initial release - -uniform float Radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform int Circle_Offset_X< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Circle_Offset_Y< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Source_Offset_X< - string label = "Source Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0.0; -uniform int Source_Offset_Y< - string label = "Source Offset Y"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0.0; - -uniform bool Antialiasing = true; -#define Smoothness 100.00 -#define AAwidth 4 - -#define uv_pi uv_pixel_interval -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; - float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; - float radius = Radius * 0.01; - float smwidth = radius * Smoothness * 0.01; - - float4 obstex = image.Sample( textureSampler, uv - soffset); - float4 color = obstex; - // Account for aspect ratio - uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; - float2 cuv = 0.5 + coffset; - float dist = distance(cuv,uv); - // Anti-aliased or pixelated edge - if( Antialiasing ) { - color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); - } else { - color.a = step( dist, radius ); - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - return float4(color.rgb, color.a); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSProfile { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] +[Alias('obs.powershell.websocket.GetProfileList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -9805,346 +6544,317 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockAnalogShader { +function Get-OBSProfileParameter { -[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] +[Alias('obs.powershell.websocket.GetProfileParameter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_ms of OBSClockAnalogShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockAnalogShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockAnalogShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockAnalogShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the hour_handle_color of OBSClockAnalogShader -[Alias('hour_handle_color')] -[ComponentModel.DefaultBindingProperty('hour_handle_color')] -[Single[]] -$HourHandleColor, -# Set the minute_handle_color of OBSClockAnalogShader -[Alias('minute_handle_color')] -[ComponentModel.DefaultBindingProperty('minute_handle_color')] -[Single[]] -$MinuteHandleColor, -# Set the second_handle_color of OBSClockAnalogShader -[Alias('second_handle_color')] -[ComponentModel.DefaultBindingProperty('second_handle_color')] -[Single[]] -$SecondHandleColor, -# Set the outline_color of OBSClockAnalogShader -[Alias('outline_color')] -[ComponentModel.DefaultBindingProperty('outline_color')] -[Single[]] -$OutlineColor, -# Set the top_line_color of OBSClockAnalogShader -[Alias('top_line_color')] -[ComponentModel.DefaultBindingProperty('top_line_color')] -[Single[]] -$TopLineColor, -# Set the background_color of OBSClockAnalogShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[Single[]] -$BackgroundColor, -# Set the time_offset_hours of OBSClockAnalogShader -[Alias('time_offset_hours')] -[ComponentModel.DefaultBindingProperty('time_offset_hours')] -[Int32] -$TimeOffsetHours, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_analog' -$ShaderNoun = 'OBSClockAnalogShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Based on https://www.shadertoy.com/view/XdKXzy -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform float3 hour_handle_color = {1.0,1.0,1.0}; -uniform float3 minute_handle_color = {1.0,1.0,1.0}; -uniform float3 second_handle_color = {1.0,0.0,0.0}; -uniform float3 outline_color = {1.0,1.0,1.0}; -uniform float3 top_line_color = {1.0,0.0,0.0}; -uniform float3 background_color = {.5,.5,.5}; -uniform int time_offset_hours = 0; - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif -// this is my first try to actually use glsl almost from scratch -// so far all i''ve done is learning by doing / reading glsl docs. -// this is inspired by my non glsl „elapsed_time“ projects -// especially this one: https://www.gottz.de/analoguhr.htm -// i will most likely use a buffer in future to calculate the elapsed_time -// aswell as to draw the background of the clock only once. -// tell me if thats a bad idea. -// update: -// screenshot: http://i.imgur.com/dF0nHDk.png -// as soon as i think its in a usefull state i''ll release the source -// of that particular c++ application on github. -// i hope sommeone might find it usefull :D + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#define PI 3.141592653589793238462643383 + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -// from https://www.shadertoy.com/view/4s3XDn <3 -float ln(float2 p, float2 a, float2 b) -{ - float2 pa = p - a; - float2 ba = b - a; - float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * h); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -// i think i should spend some elapsed_time reading docs in order to minimize this. -// hints apreciated -// (Rotated LiNe) -float rln(float2 uv, float start, float end, float perc) { - float inp = perc * PI * 2.0; - float2 coord = float2(sin(inp), cos(inp)); - return ln(uv, coord * start, coord * end); -} - -// i need this to have an alphachannel in the output -// i intend to use an optimized version of this shader for a transparent desktop widget experiment -float4 mixer(float4 c1, float4 c2) { - // please tell me if you think this would boost performance. - // the elapsed_time i implemented mix myself it sure did reduce - // the amount of operations but i''m not sure now - // if (c2.a <= 0.0) return c1; - // if (c2.a >= 1.0) return c2; - return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); - // in case you are curious how you could implement mix yourself: - // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" -float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { - if (dist <= width + shadow) { - // lets draw the shadow - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); - // now lets draw the antialiased handle - color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); - } - return color; + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 R = uv_size; - // calculate the size of a pixel - float px = 1.0 / R.y; - // create percentages of the coordinate system - float2 p = (v_in.uv * uv_size).xy / R; - // center the scene and add perspective - float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); - - /*float2 uv = -1.0 + 2.0 * p.xy; - // lets add perspective for mobile device support - if (uv_size.x > uv_size.y) - uv.x *= uv_size.x / uv_size.y; - else - uv.y *= uv_size.y / uv_size.x;*/ - - // lets scale the scene a bit down: - uv *= 1.1; - px *= 0.9; - - float width = 0.015; - float dist = 1.0; - float centerdist = length(uv); - - float4 color = image.Sample(textureSampler, v_in.uv); - - // background of the clock - if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); - - float isRed = 1.0; + +} + - if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { - // minute bars - for (float i = 0.0; i <= 15.0; i += 1.0) { - if (mod(i, 5.0) == 0.0) { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); - // draw first bar red - if (i == 0.0 && uv.y > 0.0) { - isRed = dist; - dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); - color = mixer(color, float4(top_line_color, dist)); - dist = 1.0; - } - } - else { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); - } - } +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSRecordDirectory { - // outline circle - dist = min(dist, abs(1.0-width-length(uv))); - // draw clock shadow - if (centerdist > 1.0) - color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); - // draw outline + minute bars in white - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); - color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); - } - - if (centerdist < 1.0) { - float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); - // hour - color = styleHandle(color, px, - rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), - hour_handle_color, 0.03, 0.02); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] +[Alias('obs.powershell.websocket.GetRecordDirectory')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // minute - color = styleHandle(color, px, - rln(uv, -0.075, 0.7, elapsed_time / 3600.0), - minute_handle_color, 0.02, 0.02); - // second - color = styleHandle(color, px, - min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), - second_handle_color, 0.01, 0.02); - } - - - return color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSRecordStatus { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] +[Alias('obs.powershell.websocket.GetRecordStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10153,381 +6863,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalLedShader { +function Get-OBSReplayBufferStatus { -[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] +[Alias('obs.powershell.websocket.GetReplayBufferStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_sec of OBSClockDigitalLedShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalLedShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalLedShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the showMatrix of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showMatrix')] -[Management.Automation.SwitchParameter] -$ShowMatrix, -# Set the showOff of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showOff')] -[Management.Automation.SwitchParameter] -$ShowOff, -# Set the ampm of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ampm')] -[Management.Automation.SwitchParameter] -$Ampm, -# Set the ledColor of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ledColor')] -[String] -$LedColor, -# Set the offsetHours of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] -[Int32] -$OffsetSeconds, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_digital_led' -$ShaderNoun = 'OBSClockDigitalLedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/MdfGzf -// cmarangu has linked all 7 segments in his comments -// https://www.shadertoy.com/view/3dtSRj -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform bool showMatrix = false; -uniform bool showOff = false; -uniform bool ampm = false; -uniform float4 ledColor = {1.0,0,0,1.0}; -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float segment(float2 uv, bool On) -{ - if (!On && !showOff) - return 0.0; - - float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* - (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); - - //Fiddle with lights and matrix - //uv.x += sin(elapsed_time*60.0*6.26)/14.0; - //uv.y += cos(elapsed_time*60.0*6.26)/14.0; - - //led like brightness - if (On){ - seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; - } else { - seg *= -(0.05+length(uv*float2(0.2,0.1))); - } - return seg; -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float sevenSegment(float2 uv,int num) -{ - float seg= 0.0; - seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); - seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); - seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); - seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); - seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); - seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); - seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); - - return seg; } -float showNum(float2 uv,int nr, bool zeroTrim) -{ - //Speed optimization, leave if pixel is not in segment - if (abs(uv.x)>1.5 || abs(uv.y)>1.2) - return 0.0; - - float seg= 0.0; - if (uv.x>0.0) - { - nr /= 10; - if (nr==0 && zeroTrim) - nr = -1; - seg += sevenSegment(uv+float2(-0.75,0.0),nr); - } else { - seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); - } - return seg; -} +} -float dots(float2 uv) -{ - float seg = 0.0; - uv.y -= 0.5; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - uv.y += 1.0; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - return seg; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSScene { -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / - min(uv_size.x,uv_size.y); - - if (uv_size.x>uv_size.y) - { - uv *= 6.0; - } - else - { - uv *= 12.0; - } - - uv.x *= -1.0; - uv.x += uv.y/12.0; - //wobble - //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; - //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; - uv.x += 3.5; - float seg = 0.0; - if(timeMode == 0){ - seg += showNum(uv,current_time_sec,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - seg += showNum(uv,current_time_min,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - if (ampm) { - if(current_time_hour == 0){ - seg += showNum(uv,12,true); - }else if(current_time_hour > 12){ - seg += showNum(uv,current_time_hour-12,true); - }else{ - seg += showNum(uv,current_time_hour,true); - } - } else { - seg += showNum(uv,current_time_hour,true); - } - }else{ - float timeSecs = 0.0; - if(timeMode == 1){ - timeSecs = elapsed_time_enable; - }else if(timeMode == 2){ - timeSecs = elapsed_time_active; - }else if(timeMode == 3){ - timeSecs = elapsed_time_show; - }else if(timeMode == 4){ - timeSecs = elapsed_time_start; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] +[Alias('obs.powershell.websocket.GetSceneList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - timeSecs += offsetSeconds + offsetHours*3600; - if(timeSecs < 0) - timeSecs = 0.9999-timeSecs; - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - - uv.x -= 1.75; - seg += dots(uv); - - uv.x -= 1.75; - - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - if (ampm) - { - if(timeSecs == 0.0){ - timeSecs = 12.0; - }else if (timeSecs > 12.0){ - timeSecs = mod(timeSecs,12.0); - } - }else if (timeSecs > 24.0) { - timeSecs = mod(timeSecs,24.0); - } - - uv.x -= 1.75; - - seg += dots(uv); - - uv.x -= 1.75; - seg += showNum(uv,int(mod(timeSecs,60.0)),true); - } +process { - - if (seg==0.0){ - return image.Sample(textureSampler, v_in.uv); - } - // matrix over segment - if (showMatrix) - { - seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); - //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); - } - if (seg<0.0) - { - seg = -seg;; - return float4(seg,seg,seg,1.0); - } - return float4(ledColor.rgb * seg, ledColor.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10536,658 +7069,333 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalNixieShader { +function Get-OBSSceneCollection { -[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] +[Alias('obs.powershell.websocket.GetSceneCollectionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_ms of OBSClockDigitalNixieShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockDigitalNixieShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalNixieShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalNixieShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the offsetHours of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] -[Int32] -$OffsetSeconds, -# Set the corecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('corecolor')] -[Single[]] -$Corecolor, -# Set the halocolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('halocolor')] -[Single[]] -$Halocolor, -# Set the flarecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('flarecolor')] -[Single[]] -$Flarecolor, -# Set the anodecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodecolor')] -[Single[]] -$Anodecolor, -# Set the anodehighlightscolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] -[Single[]] -$Anodehighlightscolor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_digital_nixie' -$ShaderNoun = 'OBSClockDigitalNixieShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/fsBcRm -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; -// Colors as named variables, if you want to tweak them -uniform float3 corecolor = {1.0,0.7,0.0}; -uniform float3 halocolor = {1.0,0.5,0.0}; -uniform float3 flarecolor = {1.0,0.3,0.0}; -uniform float3 anodecolor = {0.2,0.1,0.1}; -uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#define lessThan(a,b) (a < b) -#define greaterThan(a,b) (a > b) -#endif - -// psrdnoise (c) Stefan Gustavson and Ian McEwan, -// ver. 2021-12-02, published under the MIT license: -// https://github.com/stegu/psrdnoise/ -float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) -{ - float2 uv = float2(x.x+x.y*0.5, x.y); - float2 i0 = floor(uv), f0 = frac(uv); - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - float2 i1 = i0 + o1, i2 = i0 + 1.0; - float2 v0 = float2(i0.x - i0.y*0.5, i0.y); - float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); - float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); - float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; - float3 iu, iv, xw, yw; - if(any(greaterThan(period, float2(0.0,0.0)))) { - xw = float3(v0.x, v1.x, v2.x); - yw = float3(v0.y, v1.y, v2.y); - if(period.x > 0.0) - xw = mod(float3(v0.x, v1.x, v2.x), period.x); - if(period.y > 0.0) - yw = mod(float3(v0.y, v1.y, v2.y), period.y); - iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); - } else { - iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); - } - float3 hash = mod(iu, 289.0); - hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); - hash = mod((hash*34.0 + 10.0)*hash, 289.0); - float3 psi = hash*0.07482 + alpha; - float3 gx = cos(psi); float3 gy = sin(psi); - float2 g0 = float2(gx.x, gy.x); - float2 g1 = float2(gx.y, gy.y); - float2 g2 = float2(gx.z, gy.z); - float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); - w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; - float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); - float n = dot(w4, gdotx); - float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; - float2 dn0 = w4.x*g0 + dw.x*x0; - float2 dn1 = w4.y*g1 + dw.y*x1; - float2 dn2 = w4.z*g2 + dw.z*x2; - gradient = 10.9*(dn0 + dn1 + dn2); - return 10.9*n; -} - -// Compute the shortest distance from p -// to a line segment from p1 to p2. -float lined(float2 p1, float2 p2, float2 p) { - float2 p1p2 = p2 - p1; - float2 v = normalize(p1p2); - float2 s = p - p1; - float t = dot(v, s); - if (t<0.0) return length(s); - if (t>length(p1p2)) return length(p - p2); - return length(s - t*v); -} - -// Compute the shortest distance from p to a circle -// with center at c and radius r. (Extremely simple.) -float circled(float2 c, float r, float2 p) { - return abs(length(p - c) - r); -} - -// Compute the shortest distance from p to a -// circular arc with center c from p1 to p2. -// p1, p2 are in the +angle direction (ccw), -// to resolve the major/minor arc ambiguity, so -// specifying p1, p2 in the wrong order will -// yield the complement to the arc you wanted. -// If p1 = p2, the entire circle is drawn, but -// you don''t want to use this function to draw -// a circle. Use the simple circled() instead. -// If p1 and p2 have different distances to c, -// the end of the arc will not look right. If -// this is inconvenient, uncomment the 3rd line. -float arcd(float2 c, float2 p1, float2 p2, float2 p) { + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float2 v1 = p1 - c; - float2 v2 = p2 - c; - // Optional: make sure p1, p2 are both on the circle - // v2 = normalize(v2)*length(v1); - float2 v = p - c; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi - if(all(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); // nearest end - } else { - return abs(length(v) - length(v1)); // dist to arc + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - } else { // Arc angle > pi - if(any(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - return abs(length(v) - length(v1)); + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} -// A convenient anti-aliased step() using auto derivatives -float aastep(float threshold, float value) { - float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); - return smoothstep(threshold-afwidth, threshold+afwidth, value); } -// A smoothstep() that blends to an aastep() under minification -float aasmoothstep(float t1, float t2, float v) { - float aw = 0.7 * length(float2(ddx(v), ddy(v))); - float sw = max(0.5*(t2-t1), aw); - float st = 0.5*(t1+t2); - return smoothstep(st-sw, st+sw, v); -} -// Distance field of a hexagonal (simplex) grid -// The return vector contains the distances to the -// three closest points, sorted by magnitude. -float3 hexgrid(float2 p) { +} - const float stretch = 1.0/0.8660; - - // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) - p.y = p.y * stretch; - // Transform to grid space (axis-aligned "simplex" grid) - float2 uv = float2(p.x + p.y*0.5, p.y); - // Determine which simplex we''re in, with i0 being the "base" - float2 i0 = floor(uv); - float2 f0 = frac(uv); - // o1 is the offset in simplex space to the second corner - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - // Enumerate the remaining simplex corners - float2 i1 = i0 + o1; - float2 i2 = i0 + float2(1.0, 1.0); - // Transform corners back to texture space - float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); - float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); - float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); - float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); - // Only three values - bubble sort is just fine. - d.yz = (d.y < d.z) ? d.yz : d.zy; - d.xy = (d.x < d.y) ? d.xy : d.yx; - d.yz = (d.y < d.z) ? d.yz : d.zy; - return d; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItem { -// The digits. Simple functions, only a lot of them. -// These glyphs and their implementation as distance fields -// are the original work of me (stefan.gustavson@gmail.com), -// and the code below is released under the MIT license: -// https://opensource.org/licenses/MIT -// (If that is inconvenient for you, let me know. I''m reasonable.) -// -// Experts say mortals should not attempt to design character shapes. -// "It''s just ten simple digits", I thought, "How hard can it be?" -// A week later, after countless little tweaks to proportions and -// curvature, and with a notepad full of sketches and pen-and-paper -// math, some of it horribly wrong because it was decades since I -// solved this kind of equations by hand, I know the answer: -// It can be *really* hard. But also loads of fun! -// -float nixie0(float2 p) { - // Special hack instead of pasting together arcs and lines - float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); - return abs(d - 2.0); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] +[Alias('obs.powershell.websocket.GetSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float nixie1(float2 p) { - float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); - float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); - return min(d1, d2); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -float nixie1alt(float2 p) { // Straight line - return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float nixie2(float2 p) { - const float x = 3.2368345; // Icky coordinates, - const float y = 4.4283002; // used twice below - float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); - float d2 = arcd(float2(10.657842, -5.001899), // Also icky - float2(x, y), float2(-0.25, 0.0), p); - float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} -float nixie2alt(float2 p) { // Straight neck - float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); - float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); - float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} +process { -float nixie3(float2 p) { - // Two round parts: - // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); - // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); - // Angled top, more like classic Nixie tube digits: - float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); - float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); - return min(min(d1, d2), d3); -} -float nixie3alt(float2 p) { // Two round parts of the same size - float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); - float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); - return min(d1, d2); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float nixie4(float2 p) { - // This digit is 5.0 units wide, most others are 4.0 or 4.5 - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); - return min(min(d1, d2), d3); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float nixie4alt(float2 p) { - // This digit is 4.0 units wide, but looks cropped - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(d1, d2), d3); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float nixie5(float2 p) { - float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); - float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); - float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); - float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - return min(min(d1, d2), min(d3, d4)); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float nixie5alt(float2 p) { - float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); - float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); - float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); - float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); - float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); - float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float nixie6(float2 p) { - float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); - float d2 = circled(float2(2.0, 2.25), 2.25, p); - return min(d1, d2); } -float nixie6alt(float2 p) { // Straight neck - float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); - float d2 = circled(float2(2.0,2.0), 2.0, p); - return min(d1, d2); -} -float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact - float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); - return min(min(d1, d2), d3); -} +} -float nixie7alt(float2 p) { // Straight neck - float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); - return min(d1, d2); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemBlendMode { -float nixie8(float2 p) { - float d1 = circled(float2(2.0, 2.2), 2.2, p); - float d2 = circled(float2(2.0, 6.2), 1.8, p); - return min(d1, d2); -} -float nixie8alt(float2 p) { // Same size loops - float d1 = circled(float2(2.0,2.0), 2.0, p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float nixie9(float2 p) { - float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); - float d2 = circled(float2(2.0, 5.75), 2.25, p); - return min(d1, d2); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -float nixie9alt(float2 p) { // Straight neck - float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -float nixieminus(float2 p) { - return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float nixieequals(float2 p) { - float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); - float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); - return min(d1, d2); -} -float nixieplus(float2 p) { - float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); - float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); - return min(d1, d2); -} +process { -float nixiedot(float2 p) { - // circled with r=0 yields a point, but with more work - return length(p - float2(2.0, 0.0)); -} -float nixiecolon(float2 p) { - float d1 = length(p - float2(2.0,2.0)); - float d2 = length(p - float2(2.0, 5.0)); - return min(d1, d2); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// End of MIT-licensed code + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float number(int n, float2 p) { - switch(n) { - case 0: return nixie0(p); - case 1: return nixie1(p); - case 2: return nixie2(p); - case 3: return nixie3(p); - case 4: return nixie4(p); - case 5: return nixie5(p); - case 6: return nixie6(p); - case 7: return nixie7(p); - case 8: return nixie8(p); - case 9: return nixie9(p); - default: return 1e10; - } -} -// Display the current time with a retro Nixie-tube look -// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 -// All code in the "Image" tab is public domain. -// Functions in the "Common" tab are also public domain, -// except where a separate license is specified. -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = ((float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size)/uv_size.x); - float time = 0.0; - if(timeMode == 0){ - time = float(current_time_hour*3600+current_time_min*60+current_time_sec) + float(current_time_ms)/1000.0; - }else if(timeMode == 1){ - time = elapsed_time_enable; - }else if(timeMode == 2){ - time = elapsed_time_active; - }else if(timeMode == 3){ - time = elapsed_time_show; - }else if(timeMode == 4){ - time = elapsed_time_start; - } - time += offsetSeconds + offsetHours * 3600; - if(time < 0) - time = 0.9999-time; - float2 p = -3.0+uv*50.0 - float2(0.0,9.0); - - float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), - max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); - - // Some relief for the GPU: exit early if we''re in the black margins - if(bbox == 0.0) { - return float4(float3(0.0,0.0,0.0),1.0); - } - - // If not, well, let''s put that GPU to good use! - float secs = floor(mod(time, 60.0)); - float mins = floor(mod(time, 3600.0)/60.0); - float hrs = floor(time/3600.0); - int h10 = int(floor(hrs/10.0)); - int h1 = int(floor(mod(hrs, 10.0))); - int m10 = int(floor(mins/10.0)); - int m1 = int(floor(mod(mins, 10.0))); - int s10 = int(floor(secs/10.0)); - int s1 = int(floor(mod(secs, 10.0))); - - float2 wspace = float2(6.5, 0.0); - float2 nspace = float2(3.5, 0.0); - float d = 1e10; - d = min(d, number(h10, p)); - d = min(d, number(h1, p-wspace)); - d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); - d = min(d, number(m10, p-2.0*wspace-nspace)); - d = min(d, number(m1, p-3.0*wspace-nspace)); - d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); - d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); - d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); - - float2 g; // For gradients returned from psrdnoise() - - // Digit outlines - float core = 1.0-aastep(0.2, d); - // "flare" is a wide blurry region around the characters, and - // "flarenoise" is a spatio-temporal modulation of its extents - // (slight flickering, but not all characters at the same time) - float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); - float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); - flare *= flare; // A more rapid decline towards the edge - // "glow" is a variation in the intensity of the glowy cathode (core) - float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); - // Now we mess up the distance field a little for the "halo" effect - d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); - d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); - // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube - // doesn''t have this, but it adds some appealing visual detail. - // Looks more like hot filaments than "cold cathodes", but... oh, well. - float halo = 1.0-smoothstep(-0.3, 0.3, d); - - // Brittle parameters! This scale/shift of p has a strong impact - // on the pattern at the edges of the grid through "anodefade". - float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); - float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders - // Fade the hexagonal holes in the anode towards the edges - float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), - max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); - float anode = 1.0 - aastep(0.1, anodedist - anodefade); - - float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); - float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know - - float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black - mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); - mixcolor = lerp(mixcolor, halocolor, 0.9*halo); - mixcolor = lerp(mixcolor, corecolor, core*glow); - mixcolor = lerp(mixcolor, anodecolorresult, anode); - mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box - return float4(mixcolor,1.0); -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11196,172 +7404,117 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorDepthShader { +function Get-OBSSceneItemEnabled { -[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] +[Alias('obs.powershell.websocket.GetSceneItemEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the colorDepth of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('colorDepth')] -[Single] -$ColorDepth, -# Set the pixelSize of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('pixelSize')] -[Single] -$PixelSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'color-depth' -$ShaderNoun = 'OBSColorDepthShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/tscfWM -uniform float colorDepth< - string label = "Color depth"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 100.0; - float step = 0.01; -> = 5.0; - -uniform float pixelSize< - string label = "Pixel Size"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 0.01; -> = 5.0; -float4 mainImage(VertData v_in) : TARGET -{ - // Change these to change results - float2 size = uv_size / pixelSize; - float2 uv = v_in.uv; - // Maps UV onto grid of variable size to pixilate the image - uv = round(uv*size)/size; - float4 col = image.Sample(textureSampler, uv); - // Maps color onto the specified color depth - return float4(round(col.r * colorDepth) / colorDepth, - round(col.g * colorDepth) / colorDepth, - round(col.b * colorDepth) / colorDepth, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11370,225 +7523,241 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorGradeFilterShader { +function Get-OBSSceneItemId { -[Alias('Set-OBSColorGradeFilterShader','Add-OBSColorGradeFilterShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] +[Alias('obs.powershell.websocket.GetSceneItemId')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the notes of OBSColorGradeFilterShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the lut of OBSColorGradeFilterShader -[ComponentModel.DefaultBindingProperty('lut')] -[String] -$Lut, -# Set the lut_amount_percent of OBSColorGradeFilterShader -[Alias('lut_amount_percent')] -[ComponentModel.DefaultBindingProperty('lut_amount_percent')] -[Int32] -$LutAmountPercent, -# Set the lut_scale_percent of OBSColorGradeFilterShader -[Alias('lut_scale_percent')] -[ComponentModel.DefaultBindingProperty('lut_scale_percent')] -[Int32] -$LutScalePercent, -# Set the lut_offset_percent of OBSColorGradeFilterShader -[Alias('lut_offset_percent')] -[ComponentModel.DefaultBindingProperty('lut_offset_percent')] -[Int32] -$LutOffsetPercent, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('searchOffset')] +[ValidateRange(-1,[int]::MaxValue)] +[double] +$SearchOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'color_grade_filter' -$ShaderNoun = 'OBSColorGradeFilterShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Grade Filter by Charles Fettinger for obs-shaderfilter plugin 4/2020 -//https://github.com/Oncorporation/obs-shaderfilter -//OBS messed up the LUT system, this is basically the old LUT system -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform string notes< - string widget_type = "info"; -> = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; -uniform texture2d lut< - string label = "LUT"; ->; -uniform int lut_amount_percent< - string label = "LUT amount percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_scale_percent< - string label = "LUT scale percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_offset_percent< - string label = "LUT offset percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); - float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); - float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float4 textureColor = image.Sample(textureSampler, v_in.uv); - float lumaLevel = textureColor.r * 0.2126 + textureColor.g * 0.7152 + textureColor.b * 0.0722; - float blueColor = float(lumaLevel);//textureColor.b * 63.0 + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float2 quad1; - quad1.y = floor(floor(float(blueColor)) / 8.0); - quad1.x = floor(float(blueColor)) - (quad1.y * 8.0); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 quad2; - quad2.y = floor(ceil(float(blueColor)) / 8.0); - quad2.x = ceil(float(blueColor)) - (quad2.y * 8.0); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float2 texPos1; - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); +} - float2 texPos2; - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); - float4 newColor1 = lut.Sample(textureSampler, texPos1); - newColor1.rgb = newColor1.rgb * lut_scale + lut_offset; - float4 newColor2 = lut.Sample(textureSampler, texPos2); - newColor2.rgb = newColor2.rgb * lut_scale + lut_offset; - float4 luttedColor = lerp(newColor1, newColor2, frac(float(blueColor))); +} - float4 final_color = lerp(textureColor, luttedColor, lut_amount); - return float4(final_color.rgb, textureColor.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemIndex { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] +[Alias('obs.powershell.websocket.GetSceneItemIndex')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11597,358 +7766,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCornerPinShader { +function Get-OBSSceneItemLocked { -[Alias('Set-OBSCornerPinShader','Add-OBSCornerPinShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] +[Alias('obs.powershell.websocket.GetSceneItemLocked')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Antialias_Edges of OBSCornerPinShader -[Alias('Antialias_Edges')] -[ComponentModel.DefaultBindingProperty('Antialias_Edges')] -[Management.Automation.SwitchParameter] -$AntialiasEdges, -# Set the Top_Left_X of OBSCornerPinShader -[Alias('Top_Left_X')] -[ComponentModel.DefaultBindingProperty('Top_Left_X')] -[Single] -$TopLeftX, -# Set the Top_Left_Y of OBSCornerPinShader -[Alias('Top_Left_Y')] -[ComponentModel.DefaultBindingProperty('Top_Left_Y')] -[Single] -$TopLeftY, -# Set the Top_Right_X of OBSCornerPinShader -[Alias('Top_Right_X')] -[ComponentModel.DefaultBindingProperty('Top_Right_X')] -[Single] -$TopRightX, -# Set the Top_Right_Y of OBSCornerPinShader -[Alias('Top_Right_Y')] -[ComponentModel.DefaultBindingProperty('Top_Right_Y')] -[Single] -$TopRightY, -# Set the Bottom_Left_X of OBSCornerPinShader -[Alias('Bottom_Left_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] -[Single] -$BottomLeftX, -# Set the Bottom_Left_Y of OBSCornerPinShader -[Alias('Bottom_Left_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] -[Single] -$BottomLeftY, -# Set the Bottom_Right_X of OBSCornerPinShader -[Alias('Bottom_Right_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] -[Single] -$BottomRightX, -# Set the Bottom_Right_Y of OBSCornerPinShader -[Alias('Bottom_Right_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] -[Single] -$BottomRightY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'corner-pin' -$ShaderNoun = 'OBSCornerPinShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Corner Pin, Version 0.1, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Based on: -// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm -// https://www.youtube.com/c/InigoQuilez -// https://iquilezles.org/ -// and the derivative StreamFX Corner Pin effect by Xaymar -// https://github.com/Xaymar/obs-StreamFX -// -// Description: -// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily -// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying -// image with proper perspective without distortion. -// -// TODO: -// - Add feature to automatically quantize corners to pixel centers -// -// Changelog: -// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by -// Inigo Quilez that it was based upon. -uniform bool Antialias_Edges = true; -uniform float Top_Left_X< - string label = "Top left x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Left_Y< - string label = "Top left y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Right_X< - string label = "Top right x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Top_Right_Y< - string label = "Top right y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_X< - string label = "Bottom left x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_Y< - string label = "Bottom left y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_X< - string label = "Bottom right x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_Y< - string label = "Bottom right y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// DEVELOPMENTAL DEBUGGING OPTIONS -//uniform float AAstrength = 1.0; -//uniform float AAdist = 1.0; -//uniform float debug_psmult = 1.0; -//#define PIXEL_SIZE_MULT 2.0 + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float cross2d(in float2 a, in float2 b) -{ - return (a.x * b.y) - (a.y * b.x); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) -{ - float2 result = float2(-1., -1.); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 e = b - a; - float2 f = d - a; - float2 g = a-b+c-d; - float2 h = p-a; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - 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); - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemSource { - result = float2(u, v); - } - return result; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] +[Alias('obs.powershell.websocket.GetSceneItemSource')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -// distance to a line segment -float sdSegment( in float2 p, in float2 a, in float2 b ) -{ - p -= a; b -= a; - return length( p-b*saturate(dot(p,b)/dot(b,b)) ); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) -float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) -{ - //float ps = 2.0 * (2.0/uv_size.y); // Original -// float ps = debug_psmult * (2.0/uv_size.y); - float ps = (2.0/uv_size.y); -// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now - return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -float4 mainImage( VertData v_in ) : TARGET { - float2 p = 2.* v_in.uv - 1.; - - float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; - float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; - float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; - float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; - - // Convert from screen coords to potential Quad UV coordinates - float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { - return float4(0.0, 0.0, 0.0, 0.0); - } - float4 texel = image.Sample(textureSampler, uv); +process { - if ( Antialias_Edges ) { - // Anti-alias edges of texture - texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); - } - return texel; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11957,185 +8004,230 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCrtCurvatureShader { +function Get-OBSSceneItemTransform { -[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] +[Alias('obs.powershell.websocket.GetSceneItemTransform')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the strength of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# Set the border of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('border')] -[String] -$Border, -# Set the feathering of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('feathering')] -[Single] -$Feathering, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'crt-curvature' -$ShaderNoun = 'OBSCrtCurvatureShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 200.; - float step = 0.01; -> = 33.33; -uniform float4 border< - string label = "Border Color"; -> = {0., 0., 0., 1.}; -uniform float feathering< - string label = "Feathering"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 33.33; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 cc = v_in.uv - float2(0.5, 0.5); - float dist = dot(cc, cc) * strength / 100.0; - float2 bent = v_in.uv + cc * (1.0 + dist) * dist; - if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { - return border; - } - if (feathering >= .01) { - float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; - float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); - borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; - float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); - return lerp(border, image.Sample(textureSampler, bent), borderFade); - } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - return image.Sample(textureSampler, bent); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -' } -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneSceneTransitionOverride { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12144,187 +8236,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurveShader { +function Get-OBSSceneTransition { -[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] +[Alias('obs.powershell.websocket.GetSceneTransitionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the strength of OBSCurveShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# Set the scale of OBSCurveShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, -# Set the curve_color of OBSCurveShader -[Alias('curve_color')] -[ComponentModel.DefaultBindingProperty('curve_color')] -[String] -$CurveColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'curve' -$ShaderNoun = 'OBSCurveShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 - -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float4 curve_color = {0,0,0,0}; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - const float ydiff = 1.0; - uv -= float2(0.5,ydiff); - uv.x *= ( uv_size.x / uv_size.y); - uv /= scale; - if(strength > 0.0){ - float d = tan((1.0-strength)*PI/2.0); - float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); - float2 center = float2(0.0,(1.0-ydiff)+d); - float pd = distance(uv, center); - if(pd < r) - return curve_color; - float angle = atan2(center.x-uv.x,center.y-uv.y); - uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); - } - uv.x /= ( uv_size.x / uv_size.y); - uv += float2(0.5,ydiff); - return image.Sample(textureSampler,uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12333,354 +8339,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCutRectPerCornerShader { +function Get-OBSSourceActive { -[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] +[Alias('obs.powershell.websocket.GetSourceActive')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_tl of OBSCutRectPerCornerShader -[Alias('corner_tl')] -[ComponentModel.DefaultBindingProperty('corner_tl')] -[Int32] -$CornerTl, -# Set the corner_tr of OBSCutRectPerCornerShader -[Alias('corner_tr')] -[ComponentModel.DefaultBindingProperty('corner_tr')] -[Int32] -$CornerTr, -# Set the corner_br of OBSCutRectPerCornerShader -[Alias('corner_br')] -[ComponentModel.DefaultBindingProperty('corner_br')] -[Int32] -$CornerBr, -# Set the corner_bl of OBSCutRectPerCornerShader -[Alias('corner_bl')] -[ComponentModel.DefaultBindingProperty('corner_bl')] -[Int32] -$CornerBl, -# Set the border_thickness of OBSCutRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSCutRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSCutRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSCutRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSCutRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cut_rect_per_corner' -$ShaderNoun = 'OBSCutRectPerCornerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_tl< - string label = "Corner top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int corner_tr< - string label = "Corner top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int corner_br< - string label = "Corner bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int corner_bl< - string label = "Corner bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border aplha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border aplha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; - int corner_right = corner_tr>corner_br?corner_tr:corner_br; - int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; - int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = closedEdgeXabs+closedEdgeYabs; - if(d>corner_radius){ - if(d-corner_radius <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - return float4(0.0,0.0,0.0,0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12689,192 +8452,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCylinderShader { +function Get-OBSSourceFilter { -[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] +[Alias('obs.powershell.websocket.GetSourceFilter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the cylinder_factor of OBSCylinderShader -[Alias('cylinder_factor')] -[ComponentModel.DefaultBindingProperty('cylinder_factor')] -[Single] -$CylinderFactor, -# Set the background_cut of OBSCylinderShader -[Alias('background_cut')] -[ComponentModel.DefaultBindingProperty('background_cut')] -[Single] -$BackgroundCut, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cylinder' -$ShaderNoun = 'OBSCylinderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float cylinder_factor< - string label = "Cylinder factor"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.2; -uniform float background_cut< - string label = "Background cut"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.1; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.x -= 0.5; - float bend = sqrt(1.0 - uv.x*uv.x*4); - uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - uv.x += 0.5; - float4 front_color = image.Sample(textureSampler, uv); - front_color.rgb *= bend/2+0.5; - if(front_color.a >= 1.0) - return front_color; - - uv = v_in.uv; - uv.x -= 0.5; - if(abs(uv.x) < background_cut) - return front_color; - uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - if(uv.x > 0){ - uv.x = 1.0 - uv.x; - }else{ - uv.x = 0 - uv.x; - } - float4 back_color = image.Sample(textureSampler, uv); - back_color.rgb *= 0.5-bend/2; - front_color.rgb *= front_color.a; - front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; - front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; - return front_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12883,161 +8570,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDarkenShader { +function Get-OBSSourceFilterDefaultSettings { -[Alias('Set-OBSDarkenShader','Add-OBSDarkenShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] +[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Opacity_Percentage of OBSDarkenShader -[Alias('Opacity_Percentage')] -[ComponentModel.DefaultBindingProperty('Opacity_Percentage')] -[Single] -$OpacityPercentage, -# Set the Fill_Percentage of OBSDarkenShader -[Alias('Fill_Percentage')] -[ComponentModel.DefaultBindingProperty('Fill_Percentage')] -[Single] -$FillPercentage, -# Set the Notes of OBSDarkenShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'darken' -$ShaderNoun = 'OBSDarkenShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Opacity_Percentage = 100.0; -uniform float Fill_Percentage = 100.0; -uniform string Notes = "Simulates a photo editing darken layer blending mode. Fill percentage is the interior alpha and Opacity is the layer alpha."; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = float4(1.0, 1.0, 1.0, 1.0); - float4 base = image.Sample(textureSampler, v_in.uv); - float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); - float4 gray = float4(luminance,luminance,luminance, 1.0); - return min(base,other); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13046,198 +8678,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDeadPixelFixerShader { +function Get-OBSSourceFilterKind { -[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] +[Alias('obs.powershell.websocket.GetSourceFilterKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Dead_Pixel_X of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_X')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] -[Int32] -$DeadPixelX, -# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_Y')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] -[Int32] -$DeadPixelY, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'dead-pixel-fixer' -$ShaderNoun = 'OBSDeadPixelFixerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. -// The pixel located at the user configured offset will have its color overridden by taking the average of the -// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. -// -// Changelog: -// 0.01 - Initial release - -uniform int Dead_Pixel_X< - string label = "Dead Pixel X"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 2000; - int step = 1; -> = 0; -uniform int Dead_Pixel_Y< - string label = "Dead Pixel Y"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 2000; - int step = 1; -> = 0; - -float3 blur_dead_pixel(in float2 pos) -{ - float3 color; - color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; - return color * 0.25; -} -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float2 pos = v_in.pos.xy; - float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); - float4 obstex = image.Sample(textureSampler, uv); - float3 color; - - if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { - color = blur_dead_pixel(pos); - } else { - color.rgb = obstex.rgb; - } - return float4( color, obstex.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13246,488 +8781,247 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDensitySatHueShader { +function Get-OBSSourceFilterList { -[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] +[Alias('obs.powershell.websocket.GetSourceFilterList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the notes of OBSDensitySatHueShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the density_r of OBSDensitySatHueShader -[Alias('density_r')] -[ComponentModel.DefaultBindingProperty('density_r')] -[Single] -$DensityR, -# Set the saturation_r of OBSDensitySatHueShader -[Alias('saturation_r')] -[ComponentModel.DefaultBindingProperty('saturation_r')] -[Single] -$SaturationR, -# Set the hueShift_r of OBSDensitySatHueShader -[Alias('hueShift_r')] -[ComponentModel.DefaultBindingProperty('hueShift_r')] -[Single] -$HueShiftR, -# Set the density_y of OBSDensitySatHueShader -[Alias('density_y')] -[ComponentModel.DefaultBindingProperty('density_y')] -[Single] -$DensityY, -# Set the saturation_y of OBSDensitySatHueShader -[Alias('saturation_y')] -[ComponentModel.DefaultBindingProperty('saturation_y')] -[Single] -$SaturationY, -# Set the hueShift_y of OBSDensitySatHueShader -[Alias('hueShift_y')] -[ComponentModel.DefaultBindingProperty('hueShift_y')] -[Single] -$HueShiftY, -# Set the density_g of OBSDensitySatHueShader -[Alias('density_g')] -[ComponentModel.DefaultBindingProperty('density_g')] -[Single] -$DensityG, -# Set the saturation_g of OBSDensitySatHueShader -[Alias('saturation_g')] -[ComponentModel.DefaultBindingProperty('saturation_g')] -[Single] -$SaturationG, -# Set the hueShift_g of OBSDensitySatHueShader -[Alias('hueShift_g')] -[ComponentModel.DefaultBindingProperty('hueShift_g')] -[Single] -$HueShiftG, -# Set the density_c of OBSDensitySatHueShader -[Alias('density_c')] -[ComponentModel.DefaultBindingProperty('density_c')] -[Single] -$DensityC, -# Set the saturation_c of OBSDensitySatHueShader -[Alias('saturation_c')] -[ComponentModel.DefaultBindingProperty('saturation_c')] -[Single] -$SaturationC, -# Set the hueShift_c of OBSDensitySatHueShader -[Alias('hueShift_c')] -[ComponentModel.DefaultBindingProperty('hueShift_c')] -[Single] -$HueShiftC, -# Set the density_b of OBSDensitySatHueShader -[Alias('density_b')] -[ComponentModel.DefaultBindingProperty('density_b')] -[Single] -$DensityB, -# Set the saturation_b of OBSDensitySatHueShader -[Alias('saturation_b')] -[ComponentModel.DefaultBindingProperty('saturation_b')] -[Single] -$SaturationB, -# Set the hueShift_b of OBSDensitySatHueShader -[Alias('hueShift_b')] -[ComponentModel.DefaultBindingProperty('hueShift_b')] -[Single] -$HueShiftB, -# Set the density_m of OBSDensitySatHueShader -[Alias('density_m')] -[ComponentModel.DefaultBindingProperty('density_m')] -[Single] -$DensityM, -# Set the saturation_m of OBSDensitySatHueShader -[Alias('saturation_m')] -[ComponentModel.DefaultBindingProperty('saturation_m')] -[Single] -$SaturationM, -# Set the hueShift_m of OBSDensitySatHueShader -[Alias('hueShift_m')] -[ComponentModel.DefaultBindingProperty('hueShift_m')] -[Single] -$HueShiftM, -# Set the global_density of OBSDensitySatHueShader -[Alias('global_density')] -[ComponentModel.DefaultBindingProperty('global_density')] -[Single] -$GlobalDensity, -# Set the global_saturation of OBSDensitySatHueShader -[Alias('global_saturation')] -[ComponentModel.DefaultBindingProperty('global_saturation')] -[Single] -$GlobalSaturation, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'density_sat_hue' -$ShaderNoun = 'OBSDensitySatHueShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Density-Saturation-Hue Shader for OBS Shaderfilter -// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 -uniform string notes< - string widget_type = "info"; -> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; -uniform float density_r < - string label = "Red Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float saturation_r < - string label = "Red Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float hueShift_r < - string label = "Red Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float density_y < - string label = "Yellow Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float saturation_y < - string label = "Yellow Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform float hueShift_y < - string label = "Yellow Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +} -uniform float density_g < - string label = "Green Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float saturation_g < - string label = "Green Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +} -uniform float hueShift_g < - string label = "Green Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceScreenshot { -uniform float density_c < - string label = "Cyan Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float saturation_c < - string label = "Cyan Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] +[Alias('obs.powershell.websocket.GetSourceScreenshot')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -uniform float hueShift_c < - string label = "Cyan Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -uniform float density_b < - string label = "Blue Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -uniform float saturation_b < - string label = "Blue Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, -uniform float hueShift_b < - string label = "Blue Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, -uniform float density_m < - string label = "Magenta Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, -uniform float saturation_m < - string label = "Magenta Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform float hueShift_m < - string label = "Magenta Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float global_density < - string label = "Global Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +process { -uniform float global_saturation < - string label = "Global Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -// Tetrahedral interpolation based on the ordering of the input color channels -float3 applyAdjustments(float3 p) { - // Pre-calculate the flipped density values for each channel: - float d_r = -(density_r + global_density); - float d_y = -(density_y + global_density); - float d_g = -(density_g + global_density); - float d_c = -(density_c + global_density); - float d_b = -(density_b + global_density); - float d_m = -(density_m + global_density); - - // Compute the color vectors for each hue region using the flipped densities: - float3 red = float3( - 1.0 + d_r, - d_r - (saturation_r + global_saturation), - d_r + hueShift_r - (saturation_r + global_saturation) - ); - - float3 yellow = float3( - 1.0 + hueShift_y + d_y, - 1.0 + d_y, - d_y - (saturation_y + global_saturation) - ); - - float3 green = float3( - d_g - (saturation_g + global_saturation), - 1.0 + d_g, - d_g + hueShift_g - (saturation_g + global_saturation) - ); - - float3 cyan = float3( - d_c - (saturation_c + global_saturation), - 1.0 + hueShift_c + d_c, - 1.0 + d_c - ); - - float3 blue = float3( - d_b + hueShift_b - (saturation_b + global_saturation), - d_b - (saturation_b + global_saturation), - 1.0 + d_b - ); - - float3 magenta = float3( - 1.0 + d_m, - d_m - (saturation_m + global_saturation), - 1.0 + hueShift_m + d_m - ); - - // Define the black and white endpoints: - float3 blk = float3(0.0, 0.0, 0.0); - float3 wht = float3(1.0, 1.0, 1.0); - - float3 rgb; - if (p.r > p.g) { - if (p.g > p.b) { - // p.r >= p.g >= p.b - rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); - } else if (p.r > p.b) { - // p.r >= p.b > p.g - rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); - } else { - // p.b >= p.r > p.g - rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } else { - if (p.b > p.g) { - // p.b >= p.g >= p.r - rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; - } else if (p.b > p.r) { - // p.g >= p.r and p.b > p.r - rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); - } else { - // p.g >= p.b >= p.r - rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - } - return clamp(rgb, 0.0, 1.0); -} -float4 mainImage(VertData v_in) : TARGET { - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float3 adjustedColor = applyAdjustments(inputColor.rgb); - return float4(adjustedColor, inputColor.a); -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13736,242 +9030,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDiffuseTransitionShader { +function Get-OBSSpecialInputs { -[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] +[Alias('obs.powershell.websocket.GetSpecialInputs')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the image_a of OBSDiffuseTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSDiffuseTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSDiffuseTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSDiffuseTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the num_samples of OBSDiffuseTransitionShader -[Alias('num_samples')] -[ComponentModel.DefaultBindingProperty('num_samples')] -[Int32] -$NumSamples, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'diffuse_transition' -$ShaderNoun = 'OBSDiffuseTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/4cK3z1 -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -// Diffuse (move pixels) between two 2D images -// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) -// Note: the approach in this demo is different - rather than randomising paths we use means -// increase for greater precision - this is O(n^2) :( -uniform int num_samples< - string label = "Number of samples (10)"; - string widget_type = "slider"; - int minimum = 2; - int maximum = 100; - int step = 1; -> = 10; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (transition_time < 0.00001) { - return image_a.Sample(textureSampler, uv); - } - - // we need to normalise the distributions so just sum the samples for a division later - // note: could calculate this once per image in a buffer or something - float3 from_total = float3(0.0,0.0,0.0); - float3 to_total = float3(0.0,0.0,0.0); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - for (int i=0; iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13980,232 +9236,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDigitalRainShader { +function Get-OBSStreamServiceSettings { -[Alias('Set-OBSDigitalRainShader','Add-OBSDigitalRainShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] +[Alias('obs.powershell.websocket.GetStreamServiceSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the font of OBSDigitalRainShader -[ComponentModel.DefaultBindingProperty('font')] -[String] -$Font, -# Set the noise of OBSDigitalRainShader -[ComponentModel.DefaultBindingProperty('noise')] -[String] -$Noise, -# Set the base_color of OBSDigitalRainShader -[Alias('base_color')] -[ComponentModel.DefaultBindingProperty('base_color')] -[String] -$BaseColor, -# Set the rain_speed of OBSDigitalRainShader -[Alias('rain_speed')] -[ComponentModel.DefaultBindingProperty('rain_speed')] -[Single] -$RainSpeed, -# Set the char_speed of OBSDigitalRainShader -[Alias('char_speed')] -[ComponentModel.DefaultBindingProperty('char_speed')] -[Single] -$CharSpeed, -# Set the glow_contrast of OBSDigitalRainShader -[Alias('glow_contrast')] -[ComponentModel.DefaultBindingProperty('glow_contrast')] -[Single] -$GlowContrast, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'digital-rain' -$ShaderNoun = 'OBSDigitalRainShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/ldccW4 by WillKirkby -#ifndef OPENGL -#define fract frac -#define mix lerp -#endif + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform texture2d font = "font.png"; -uniform texture2d noise = "noise.png"; -uniform float4 base_color = {.1, 1.0, .35, 1.0}; -uniform float rain_speed< - string label = "Rain Speed"; - string widget_type = "slider"; - float minimum = 0.001; - float maximum = 2.0; - float step = .001; -> = 1.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float char_speed< - string label = "Character Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = .001; -> = 1.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float glow_contrast< - string label = "Glow contrast"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.5; - float step = .001; -> = 1.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float mod(float x, float y) -{ - return x - y * floor(x/y); } -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x/y); -} -float text(float2 fragCoord) -{ - float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; - float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; - uv = uv*.8+.1; // scale the letters up a bit - block += elapsed_time*.002*char_speed; - uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters - uv *= .0625; // bring back into 0-1 range - return font.Sample(textureSampler, uv).r; -} +} -float3 rain(float2 fragCoord) -{ - fragCoord.x -= mod(fragCoord.x, 16.); - float offset=sin(fragCoord.x*15.); - float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; - - float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); - return base_color.rgb / pow(y*20.0, glow_contrast); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStreamStatus { -float4 mainImage(VertData v_in) : TARGET -{ - return mix(image.Sample(textureSampler, v_in.uv),float4(rain(float2(v_in.uv.x,1.0-v_in.uv.y)*uv_size),1.0), text(v_in.uv*uv_size)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] +[Alias('obs.powershell.websocket.GetStreamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14214,236 +9442,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDivideRotateShader { +function Get-OBSStudioModeEnabled { -[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] +[Alias('obs.powershell.websocket.GetStudioModeEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the iChannel0 of OBSDivideRotateShader -[ComponentModel.DefaultBindingProperty('iChannel0')] -[String] -$IChannel0, -# Set the speed_percentage of OBSDivideRotateShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSDivideRotateShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSDivideRotateShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'divide_rotate' -$ShaderNoun = 'OBSDivideRotateShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// divide and rotate shader for OBS Studio shaderfilter plugin -// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) -// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform texture2d iChannel0; -uniform int speed_percentage< - string label = "Speed"; - string widget_type = "slider"; - int minimum = -10; - int maximum = 10; - int step = 1; -> = 5; -uniform int alpha_percentage< - string label = "Opacity Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = true; -uniform string notes< - string widget_type = "info"; -> = "add rotation and speed"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float2 cm(float2 a, float2 b) { - return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float2 iter(float2 uv, float2 rot, float scale) { - float2 gv = frac(cm(uv, rot) * scale); - float boundDist = 1. - max(abs(gv.x), abs(gv.y)); - float mask = step(.03, boundDist); - gv *= mask; - return gv; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); - float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - // Normalize coords - //float2 uv = (v_in.uv * uv_scale + uv_offset); - float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; - float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // Add some time rotation and offset - float t = elapsed_time * speed; - float2 time = float2(sin(t), cos(t)); - uv += time; +} - // Imaginary component has to be mirrored for natural feeling rotation - mouse.y *= -1.0; - // Draw few layers of this to bend space - float2 rot = cm(mouse, time); - for (float i=1.0; i<=3.0; i++) { - uv = iter(uv, rot, 1.5); - } +} - // Combine background with new image - float4 background_color = image.Sample(textureSampler, v_in.uv); - float4 col = iChannel0.Sample(textureSampler, uv); + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSTransitionKind { - // Border - if (uv.x == 0.0 && uv.y == 0.0) { - col = float4(0,0,0,alpha); - } - // if not appling to alpha layer, set output alpha - if (Apply_To_Alpha_Layer == false) - col.a = alpha; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetTransitionKindList')] +[Alias('obs.powershell.websocket.GetTransitionKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - //output color is combined with background image - col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +process { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14452,271 +9648,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDoodleShader { +function Get-OBSVersion { -[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVersion')] +[Alias('obs.powershell.websocket.GetVersion')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSDoodleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSDoodleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSDoodleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSDoodleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSDoodleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSDoodleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the Doodle_Scale_Percent of OBSDoodleShader -[Alias('Doodle_Scale_Percent')] -[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] -[Single] -$DoodleScalePercent, -# Set the Snap_Percent of OBSDoodleShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] -[Single] -$SnapPercent, -# Set the Notes of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'doodle' -$ShaderNoun = 'OBSDoodleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; - -uniform float Doodle_Scale_Percent< - string label = "Doodle Scale Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 2.5; -uniform float Snap_Percent< - string label = "Snap Percent"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 0.1; -> = 7.5; -uniform string Notes< - string widget_type = "info"; -> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -float3 rand3(float3 co) -{ - float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = frac(512.0*j); - j *= .125; - result.x = frac(512.0*j); - j *= .125; - result.y = frac(512.0*j); - return result - 0.5; -} - -float snap(float x, float snap) -{ - return snap * round(x / max(0.01,snap)); -} - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); - float rand = snap(rand_f, Snap_Percent *.01); - float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); - vert_out.uv.xy += noise; - - return vert_out; -} - -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} -technique Draw -{ - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14725,275 +9751,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrawingsShader { +function Get-OBSVideoSettings { -[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] +[Alias('obs.powershell.websocket.GetVideoSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the AngleNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('AngleNum')] -[Int32] -$AngleNum, -# Set the SampNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('SampNum')] -[Int32] -$SampNum, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'drawings' -$ShaderNoun = 'OBSDrawingsShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldlcWs -uniform int AngleNum< - string label = "Number of angles"; - string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; - int step = 1; -> = 3; -uniform int SampNum< - string label = "Number of samples"; - string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; - int step = 1; -> = 9; -float4 getCol(float2 pos) -{ - // take aspect ratio into account - float2 uv=pos; - float4 c1=image.Sample(textureSampler, uv); - float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); - c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); - float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); - float4 c2=float4(.7,.7,.7,.7); - return min(lerp(c1,c2,1.8*d),.7); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 getColHT(float2 pos) -{ - return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float getVal(float2 pos) -{ - float4 c=getCol(pos); - return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float2 getGrad(float2 pos, float eps) -{ - float2 d=float2(eps,0.); - return float2( - getVal(pos+d.xy)-getVal(pos-d.xy), - getVal(pos+d.yx)-getVal(pos-d.yx) - )/eps/2.; } - float lum( float3 c) { - return dot(c, float3(0.3, 0.59, 0.11)); - } +} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSVirtualCamStatus { - float3 clipcolor( float3 c) { - float l = lum(c); - float n = min(min(c.r, c.g), c.b); - float x = max(max(c.r, c.g), c.b); - - if (n < 0.0) { - c.r = l + ((c.r - l) * l) / (l - n); - c.g = l + ((c.g - l) * l) / (l - n); - c.b = l + ((c.b - l) * l) / (l - n); - } - if (x > 1.25) { - c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); - c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); - c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); - } - return c; - } - float3 setlum( float3 c, float l) { - float d = l - lum(c); - c = c + float3(d,d,d); - return clipcolor(0.85*c); - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] +[Alias('obs.powershell.websocket.GetVirtualCamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - float3 col = float3(0,0,0); - float3 col2 = float3(0,0,0); - float sum=0.; - - for(int i=0;iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15002,218 +9957,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDropShadowShader { +function Open-OBSInputFiltersDialog { -[Alias('Set-OBSDropShadowShader','Add-OBSDropShadowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] +[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] param( -# Set the shadow_offset_x of OBSDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] -[Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] -[Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] -[Int32] -$ShadowBlurSize, -# Set the notes of OBSDropShadowShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the shadow_color of OBSDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] -[String] -$ShadowColor, -# Set the is_alpha_premultiplied of OBSDropShadowShader -[Alias('is_alpha_premultiplied')] -[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] -[Management.Automation.SwitchParameter] -$IsAlphaPremultiplied, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'drop_shadow' -$ShaderNoun = 'OBSDropShadowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Drop Shadow shader modified by Charles Fettinger -// impose a limiter to keep from crashing the system -//Converted to OpenGL by Exeldro February 19, 2022 -uniform int shadow_offset_x< - string label = "Shadow offset x"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 5; -uniform int shadow_offset_y< - string label = "Shadow offset y"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 5; -uniform int shadow_blur_size< - string label = "Shadow blur size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 15; - int step = 1; -> = 3; -uniform string notes< - string widget_type = "info"; -> = "blur size is limited to a max of 15 to ensure GPU"; -uniform float4 shadow_color; -uniform bool is_alpha_premultiplied; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); - int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); - - float sampled_shadow_alpha = 0.0; - - for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) - { - for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); - return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15222,336 +10069,344 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrunkShader { +function Open-OBSInputInteractDialog { -[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] +[Alias('obs.powershell.websocket.OpenInputInteractDialog')] param( -# Set the color_matrix of OBSDrunkShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the glow_percent of OBSDrunkShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSDrunkShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSDrunkShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed_percent of OBSDrunkShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the Apply_To_Alpha_Layer of OBSDrunkShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the glow_color of OBSDrunkShader -[Alias('glow_color')] -[ComponentModel.DefaultBindingProperty('glow_color')] -[String] -$GlowColor, -# Set the ease of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'drunk' -$ShaderNoun = 'OBSDrunkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -uniform float4x4 color_matrix; -uniform int glow_percent< - string label = "Glow percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; -uniform float4 glow_color; -uniform bool ease; -uniform bool glitch; -uniform string notes< - string widget_type = "info"; -> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -// Gaussian Blur -float Gaussian(float x, float o) { - const float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; } -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 offsets[4]; - offsets[0] = float2(-0.05, 0.066); - offsets[1] = float2(-0.05, -0.066); - offsets[2] = float2(0.05, -0.066); - offsets[3] = float2(0.05, 0.066); +} - // convert input for vector math - float blur_amount = float(blur) /100; - float glow_amount = float(glow_percent) * 0.1; - float speed = float(pulse_speed_percent) * 0.01; - float luminance_floor = float(min_brightness) * 0.01; - float luminance_ceiling = float(max_brightness) * 0.01; + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSInputPropertiesDialog { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 temp_color = color; - bool glitch_on = glitch; - //circular easing variable - float t = 1 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] +[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] +param( - //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); - float4 glitch_color = glow_color; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - for (int n = 0; n < 4; n++){ - //blur sample - b = BlurStyler(t,0,c,d,ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - //test for rand_f color - if (glitch) { - glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); - if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) - { - glitch_on = true; - } - } - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance - ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color - glitch_on) //test for rand color - { - //glow calc - if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); - //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); +process { - // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color - // max is used to simulate addition of vector texture color - temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time - max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time - * (glitch_color * (b / 2)); // glow color affected by glow over time - } - } - } - // grab lighter color - return max(color,temp_color); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - continue nextParameter - } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSSourceProjector { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] +[Alias('obs.powershell.websocket.OpenSourceProjector')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15560,470 +10415,227 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDynamicMaskShader { +function Open-OBSVideoMixProjector { -[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] +[Alias('obs.powershell.websocket.OpenVideoMixProjector')] param( -# Set the input_source of OBSDynamicMaskShader -[Alias('input_source')] -[ComponentModel.DefaultBindingProperty('input_source')] -[String] -$InputSource, -# Set the red_base_value of OBSDynamicMaskShader -[Alias('red_base_value')] -[ComponentModel.DefaultBindingProperty('red_base_value')] -[Single] -$RedBaseValue, -# Set the red_red_input_value of OBSDynamicMaskShader -[Alias('red_red_input_value')] -[ComponentModel.DefaultBindingProperty('red_red_input_value')] -[Single] -$RedRedInputValue, -# Set the red_green_input_value of OBSDynamicMaskShader -[Alias('red_green_input_value')] -[ComponentModel.DefaultBindingProperty('red_green_input_value')] -[Single] -$RedGreenInputValue, -# Set the red_blue_input_value of OBSDynamicMaskShader -[Alias('red_blue_input_value')] -[ComponentModel.DefaultBindingProperty('red_blue_input_value')] -[Single] -$RedBlueInputValue, -# Set the red_alpha_input_value of OBSDynamicMaskShader -[Alias('red_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] -[Single] -$RedAlphaInputValue, -# Set the red_multiplier of OBSDynamicMaskShader -[Alias('red_multiplier')] -[ComponentModel.DefaultBindingProperty('red_multiplier')] -[Single] -$RedMultiplier, -# Set the green_base_value of OBSDynamicMaskShader -[Alias('green_base_value')] -[ComponentModel.DefaultBindingProperty('green_base_value')] -[Single] -$GreenBaseValue, -# Set the green_red_input_value of OBSDynamicMaskShader -[Alias('green_red_input_value')] -[ComponentModel.DefaultBindingProperty('green_red_input_value')] -[Single] -$GreenRedInputValue, -# Set the green_green_input_value of OBSDynamicMaskShader -[Alias('green_green_input_value')] -[ComponentModel.DefaultBindingProperty('green_green_input_value')] -[Single] -$GreenGreenInputValue, -# Set the green_blue_input_value of OBSDynamicMaskShader -[Alias('green_blue_input_value')] -[ComponentModel.DefaultBindingProperty('green_blue_input_value')] -[Single] -$GreenBlueInputValue, -# Set the green_alpha_input_value of OBSDynamicMaskShader -[Alias('green_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] -[Single] -$GreenAlphaInputValue, -# Set the green_multiplier of OBSDynamicMaskShader -[Alias('green_multiplier')] -[ComponentModel.DefaultBindingProperty('green_multiplier')] -[Single] -$GreenMultiplier, -# Set the blue_base_value of OBSDynamicMaskShader -[Alias('blue_base_value')] -[ComponentModel.DefaultBindingProperty('blue_base_value')] -[Single] -$BlueBaseValue, -# Set the blue_red_input_value of OBSDynamicMaskShader -[Alias('blue_red_input_value')] -[ComponentModel.DefaultBindingProperty('blue_red_input_value')] -[Single] -$BlueRedInputValue, -# Set the blue_green_input_value of OBSDynamicMaskShader -[Alias('blue_green_input_value')] -[ComponentModel.DefaultBindingProperty('blue_green_input_value')] -[Single] -$BlueGreenInputValue, -# Set the blue_blue_input_value of OBSDynamicMaskShader -[Alias('blue_blue_input_value')] -[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] -[Single] -$BlueBlueInputValue, -# Set the blue_alpha_input_value of OBSDynamicMaskShader -[Alias('blue_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] -[Single] -$BlueAlphaInputValue, -# Set the blue_multiplier of OBSDynamicMaskShader -[Alias('blue_multiplier')] -[ComponentModel.DefaultBindingProperty('blue_multiplier')] -[Single] -$BlueMultiplier, -# Set the alpha_base_value of OBSDynamicMaskShader -[Alias('alpha_base_value')] -[ComponentModel.DefaultBindingProperty('alpha_base_value')] -[Single] -$AlphaBaseValue, -# Set the alpha_red_input_value of OBSDynamicMaskShader -[Alias('alpha_red_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] -[Single] -$AlphaRedInputValue, -# Set the alpha_green_input_value of OBSDynamicMaskShader -[Alias('alpha_green_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] -[Single] -$AlphaGreenInputValue, -# Set the alpha_blue_input_value of OBSDynamicMaskShader -[Alias('alpha_blue_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] -[Single] -$AlphaBlueInputValue, -# Set the alpha_alpha_input_value of OBSDynamicMaskShader -[Alias('alpha_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] -[Single] -$AlphaAlphaInputValue, -# Set the alpha_multiplier of OBSDynamicMaskShader -[Alias('alpha_multiplier')] -[ComponentModel.DefaultBindingProperty('alpha_multiplier')] -[Single] -$AlphaMultiplier, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('videoMixType')] +[string] +$VideoMixType, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'dynamic-mask' -$ShaderNoun = 'OBSDynamicMaskShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d input_source< - string label = "Input Source"; ->; -uniform float red_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float red_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float green_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float green_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float blue_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float blue_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; - -uniform float alpha_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float alpha_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 input_color = input_source.Sample(textureSampler, v_in.uv); - float4 mask; - mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; - mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; - mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; - mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; - float4 base = image.Sample(textureSampler, v_in.uv); - return base * mask; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSInput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] +[Alias('obs.powershell.websocket.RemoveInput')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -16032,278 +10644,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEdgeDetectionShader { +function Remove-OBSProfile { -[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] +[Alias('obs.powershell.websocket.RemoveProfile')] param( -# Set the sensitivity of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('sensitivity')] -[Single] -$Sensitivity, -# Set the invert_edge of OBSEdgeDetectionShader -[Alias('invert_edge')] -[ComponentModel.DefaultBindingProperty('invert_edge')] -[Management.Automation.SwitchParameter] -$InvertEdge, -# Set the edge_color of OBSEdgeDetectionShader -[Alias('edge_color')] -[ComponentModel.DefaultBindingProperty('edge_color')] -[String] -$EdgeColor, -# Set the edge_multiply of OBSEdgeDetectionShader -[Alias('edge_multiply')] -[ComponentModel.DefaultBindingProperty('edge_multiply')] -[Management.Automation.SwitchParameter] -$EdgeMultiply, -# Set the non_edge_color of OBSEdgeDetectionShader -[Alias('non_edge_color')] -[ComponentModel.DefaultBindingProperty('non_edge_color')] -[String] -$NonEdgeColor, -# Set the non_edge_multiply of OBSEdgeDetectionShader -[Alias('non_edge_multiply')] -[ComponentModel.DefaultBindingProperty('non_edge_multiply')] -[Management.Automation.SwitchParameter] -$NonEdgeMultiply, -# Set the alpha_channel of OBSEdgeDetectionShader -[Alias('alpha_channel')] -[ComponentModel.DefaultBindingProperty('alpha_channel')] -[Management.Automation.SwitchParameter] -$AlphaChannel, -# Set the alpha_level of OBSEdgeDetectionShader -[Alias('alpha_level')] -[ComponentModel.DefaultBindingProperty('alpha_level')] -[Single] -$AlphaLevel, -# Set the alpha_invert of OBSEdgeDetectionShader -[Alias('alpha_invert')] -[ComponentModel.DefaultBindingProperty('alpha_invert')] -[Management.Automation.SwitchParameter] -$AlphaInvert, -# Set the rand_f of OBSEdgeDetectionShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the notes of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'edge_detection' -$ShaderNoun = 'OBSEdgeDetectionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Edge Detection for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform float sensitivity< - string label = "Sensitivity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.05; -uniform bool invert_edge; -uniform float4 edge_color = {1.0,1.0,1.0,1.0}; -uniform bool edge_multiply; -uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; -uniform bool non_edge_multiply; -uniform bool alpha_channel; -uniform float alpha_level< - string label = "Alpha level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool alpha_invert; -uniform float rand_f; - -uniform string notes< - string widget_type = "info"; -> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - - float s = 3; - float hstep = uv_pixel_interval.x; - float vstep = uv_pixel_interval.y; - - float offsetx = (hstep * s) / 2.0; - float offsety = (vstep * s) / 2.0; - - float4 lum = float4(0.30, 0.59, 0.11, 1 ); - float samples[9]; - - int index = 0; - for(int i = 0; i < s; i++){ - for(int j = 0; j < s; j++){ - samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); - index++; - } - } - - float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; - float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; - float4 col; - - float o = ((vert * vert) + (hori * hori)); - bool isEdge = o > sensitivity; - if(invert_edge){ - isEdge = !isEdge; - } - if(isEdge) { - col = edge_color; - if(edge_multiply){ - col *= c; - } - } else { - col = non_edge_color; - if(non_edge_multiply){ - col *= c; - } - } - if (alpha_invert) { - lum = 1.0 - lum; - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if(alpha_channel){ - if (edge_multiply && isEdge) { - return clamp(lerp(c, col, alpha_level), 0.0, 1.0); - } - else { - // use max instead of multiply - return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); - } - } else { - // col.a = col.a * alpha_level; - return col; - } -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -16312,599 +10751,345 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbersShader { +function Remove-OBSScene { -[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] +[Alias('obs.powershell.websocket.RemoveScene')] param( -# Set the ViewProj of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSEmbersShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSEmbersShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSEmbersShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSEmbersShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSEmbersShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSEmbersShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSEmbersShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSEmbersShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSEmbersShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the notes of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Speed of OBSEmbersShader -[Alias('Animation_Speed')] -[ComponentModel.DefaultBindingProperty('Animation_Speed')] -[Single] -$AnimationSpeed, -# Set the Movement_Direction_Horizontal of OBSEmbersShader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] -[Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSEmbersShader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] -[Single] -$MovementDirectionVertical, -# Set the Movement_Speed_Percent of OBSEmbersShader -[Alias('Movement_Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] -[Int32] -$MovementSpeedPercent, -# Set the Layers_Count of OBSEmbersShader -[Alias('Layers_Count')] -[ComponentModel.DefaultBindingProperty('Layers_Count')] -[Int32] -$LayersCount, -# Set the lumaMin of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Alpha_Percentage of OBSEmbersShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSEmbersShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'embers' -$ShaderNoun = 'OBSEmbersShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -uniform string notes< - string widget_type = "info"; -> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float Animation_Speed < - string label = "Animation Speed"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; - float scale = 1.; -> = 1.5; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 5.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 10.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform int Movement_Speed_Percent< - string label = "Movement Speed Percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 5; +} -uniform int Layers_Count < - string label = "Layers"; - string widget_type = "slider"; - int minimum = 1.0; - int maximum = 100.0; - int step = 1; -> = 15; -/* ps start -*/ +} -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform bool Apply_To_Alpha_Layer = true; + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSSceneItem { -#define PI 3.1415927 -#define TWO_PI 6.283185 -#define PARTICLE_SIZE 0.009 +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] +[Alias('obs.powershell.websocket.RemoveSceneItem')] +param( -#define PARTICLE_SCALE float2(0.5, 1.6) -#define PARTICLE_SCALE_VAR float2(0.25, 0.2) +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) -#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 -#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 -#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -#define SIZE_MOD 1.05 -#define ALPHA_MOD 0.9 -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) -#define Movement_Speed Movement_Speed_Percent * 0.01 -#define UV float2(fragCoord.xy / uv_size) -float hash1_2(float2 x) -{ - return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); -} +process { -float2 hash2_2(float2 x) -{ - mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); - float2 y = mul(x, m); - return fract(sin(y) * 492.194); -} -//Simple interpolated noise -float2 noise2_2(float2 uv) -{ - //float2 f = fract(uv); - float2 f = smoothstep(0.0, 1.0, fract(uv)); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - float2 v00 = hash2_2(uv00); - float2 v01 = hash2_2(uv01); - float2 v10 = hash2_2(uv10); - float2 v11 = hash2_2(uv11); - - float2 v0 = mix(v00, v01, f.y); - float2 v1 = mix(v10, v11, f.y); - float2 v = mix(v0, v1, f.x); - - return v; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -//Simple interpolated noise -float noise1_2(float2 uv) -{ - float2 f = fract(uv); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - - float v00 = hash1_2(uv00); - float v01 = hash1_2(uv01); - float v10 = hash1_2(uv10); - float v11 = hash1_2(uv11); - - float v0 = mix(v00, v01, f.y); - float v1 = mix(v10, v11, f.y); - float v = mix(v0, v1, f.x); - - return v; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) -{ - float noise = 0.0; - float alpha = 1.0; - float size = 1.0; - float2 offset; - for (int i = 0; i < layers; i++) - { - offset += hash2_2(float2(alpha, size)) * 10.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } - //Adding noise with movement - noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; - alpha *= alphaMod; - size *= sizeMod; - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); - return noise; -} - -//Rotates point around 0,0 -float2 rotate(float2 vpoint, float deg) -{ - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - return mul(vpoint, m); -} - -//Cell center from point on the grid -float2 voronoiPointFromRoot(float2 root, float deg) -{ - float2 vpoint = hash2_2(root) - 0.5; - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - vpoint = mul(vpoint, m) * 0.66; - vpoint += root + 0.5; - return vpoint; -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -//Voronoi cell point rotation degrees -float degFromRootUV(in float2 uv) -{ - return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) -{ - return vpoint + (hash2_2(uv) - 0.5) * range; } -float3 fireParticles(in float2 uv, in float2 originalUV) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float2 rootUV = floor(uv); - float deg = degFromRootUV(rootUV); - float2 pointUV = voronoiPointFromRoot(rootUV, deg); - float dist = 2.0; - float distBloom = 0.0; - - //UV manipulation for the faster particle movement - float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; - tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; - - //Sparks sdf - dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); - - //Bloom sdf - distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); - - //Add sparks - particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; - - //Add bloom - particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; - - //Upper disappear curve randomization - float border = (hash1_2(rootUV) - 0.5) * 2.0; - float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); - - //Lower appear curve randomization - border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; - float appear = smoothstep(border, border + 0.4, originalUV.y); - - return particles * disappear * appear; -} - +} -//Layering particles to imitate 3D view -float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float size = 1.0; - float alpha = 1.0; - float2 offset = float2(0.0, 0.0); - float2 noiseOffset; - float2 bokehUV; - - for (int i = 0; i < layers; i++) - { - //Particle noise movement - noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; - - //UV with applied movement - bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; - - //Adding particles if there is more smoke, remove smaller particles - particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); - - //Moving uv origin to avoid generating the same particles - offset += hash2_2(float2(alpha, alpha)) * 10.0; - - alpha *= alphaMod; - size *= sizeMod; - } - - return particles; -} + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSSourceFilter { -void mainImage(out float4 fragColor, in float2 fragCoord) -{ - float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; - float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); - - uv *= 1.8; - float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); - - float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); - smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); - float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; - - //Cutting holes in smoke - smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), - 2.0) * 1.5; - - float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); - - float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); - col.rgb *= vignette; - col.rgb = smoothstep(-0.08, 1.0, col.rgb); - - if (Apply_To_Alpha_Layer) - { - float4 original_color = image.Sample(textureSampler, UV); - - float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min, 0.0, 1.0); - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] +[Alias('obs.powershell.websocket.RemoveSourceFilter')] +param( - fragColor = col; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -/*ps end*/ +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} +process { -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -16913,242 +11098,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossColorShader { +function Resume-OBSRecord { -[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] +[Alias('obs.powershell.websocket.ResumeRecord')] param( -# Set the Angle_Steps of OBSEmbossColorShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSEmbossColorShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSEmbossColorShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the Up_Down_Percent of OBSEmbossColorShader -[Alias('Up_Down_Percent')] -[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] -[Int32] -$UpDownPercent, -# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSEmbossColorShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'emboss_color' -$ShaderNoun = 'OBSEmbossColorShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 9; // -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 4; // -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 12.0; -uniform int Up_Down_Percent< - string label = "Up Down Percent"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (1 * uv_pixel_interval.y); - float maxRadius = (6 * uv_pixel_interval.y); - - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - float embossAngle = 0.25 * PI; - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); - - if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) - { - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; - - for (float angle = 0; angle < (2 * PI); angle += angleDelta) { - float2 currentCoord; - - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; - - } - } - accumulatedColor *= ampFactor; - c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); - } - //return c0 + accumulatedColor; // down; - //return c0 - accumulatedColor; // up - return c0; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17157,177 +11200,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossShader { +function Save-OBSReplayBuffer { -[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] +[Alias('obs.powershell.websocket.SaveReplayBuffer')] param( -# Set the Use_Color of OBSEmbossShader -[Alias('Use_Color')] -[ComponentModel.DefaultBindingProperty('Use_Color')] -[Management.Automation.SwitchParameter] -$UseColor, -# Set the Apply_To_Alpha_Layer of OBSEmbossShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'emboss' -$ShaderNoun = 'OBSEmbossShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform bool Use_Color; -uniform bool Apply_To_Alpha_Layer = true; - -float4 mainImage(VertData v_in) : TARGET -{ - - float dx = 1 / uv_size.x; - float dy = 1 / uv_size.y; - - float4 c0 = image.Sample(textureSampler, v_in.uv); - if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) - { - float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); - float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); - float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); - float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); - float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); - float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); - - c0 = (-c1 - c2 - c4 + c6 + c8 + c9); - float c = (c0.r + c0.g + c0.b) / 3 + 0.5; - c0 = float4(c,c,c,c); - if (Use_Color) - { - float4 rgba = image.Sample(textureSampler, v_in.uv); - return (0.5 * rgba) + c0; - } - } - return c0; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17336,301 +11302,146 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSExeldroBentCameraShader { +function Save-OBSSourceScreenshot { -[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] +[Alias('obs.powershell.websocket.SaveSourceScreenshot')] param( -# Set the left_side_width of OBSExeldroBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSExeldroBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSExeldroBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSExeldroBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSExeldroBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSExeldroBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSExeldroBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSExeldroBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] -[Single] -$RightSideShadow, -# Set the right_flip_width of OBSExeldroBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] -[Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSExeldroBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] -[Single] -$RightFlipShadow, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFilePath')] +[string] +$ImageFilePath, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'exeldro-bent-camera' -$ShaderNoun = 'OBSExeldroBentCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; - -uniform float right_side_width< - string label = "Right side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width / 2.0; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width / 2.0; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; - } - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; - } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} + + Get-Item $paramCopy["imageFilePath"] | + Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | + Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru + } @@ -17638,173 +11449,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFadeTransitionShader { +function Send-OBSCallVendorRequest { -[Alias('Set-OBSFadeTransitionShader','Add-OBSFadeTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] +[Alias('obs.powershell.websocket.CallVendorRequest')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the image_a of OBSFadeTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSFadeTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSFadeTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSFadeTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('vendorName')] +[string] +$VendorName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('requestType')] +[string] +$RequestType, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('requestData')] +[PSObject] +$RequestData, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fade-transition' -$ShaderNoun = 'OBSFadeTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 a_val = image_a.Sample(textureSampler, v_in.uv); - float4 b_val = image_b.Sample(textureSampler, v_in.uv); - float4 rgba = lerp(a_val, b_val, transition_time); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17813,230 +11567,222 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorGradientShader { +function Send-OBSCustomEvent { -[Alias('Set-OBSFillColorGradientShader','Add-OBSFillColorGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] +[Alias('obs.powershell.websocket.BroadcastCustomEvent')] param( -# Set the Fill of OBSFillColorGradientShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Gradient_Width of OBSFillColorGradientShader -[Alias('Gradient_Width')] -[ComponentModel.DefaultBindingProperty('Gradient_Width')] -[Single] -$GradientWidth, -# Set the Gradient_Offset of OBSFillColorGradientShader -[Alias('Gradient_Offset')] -[ComponentModel.DefaultBindingProperty('Gradient_Offset')] -[Single] -$GradientOffset, -# Set the Fill_Direction of OBSFillColorGradientShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill_Color of OBSFillColorGradientShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('eventData')] +[PSObject] +$EventData, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_gradient' -$ShaderNoun = 'OBSFillColorGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.005; -> = 0.500; -uniform float Gradient_Width< - string label = "Gradient Width"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 0.15; // Adjust the maximum value as needed - float step = 0.01; -> = 0.05; -uniform float Gradient_Offset< - string label = "Gradient Offset"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 0.100; // Adjust the maximum value as needed - float step = 0.005; -> = 0.00; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Bottom"; - int option_3_value = 3; - string option_3_label = "Top"; -> = 0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float4 Fill_Color; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float distanceToEdge = 0.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - // Calculate distance to the fill edge based on the selected direction - if (Fill_Direction == 0) - distanceToEdge = Fill - v_in.uv.x; - else if (Fill_Direction == 1) - distanceToEdge = v_in.uv.x - (1.0 - Fill); - else if (Fill_Direction == 2) - distanceToEdge = v_in.uv.y - (1.0 - Fill); - else if (Fill_Direction == 3) - distanceToEdge = Fill - v_in.uv.y; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // Calculate the gradient factor based on the distance to the edge and the gradient width - float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); - float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; +} - // Adjust distanceToEdge by the Gradient_Offset - distanceToEdge += gradientOffset; - // Normalize the distance to be between 0 and 1 - distanceToEdge = saturate(distanceToEdge); +} - // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); - // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); - float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSOffsetMediaInputCursor { - // Blend between the fill color and the original image color using the gradient factor - float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); - return finalColor; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] +[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +param( -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] +[double] +$MediaCursorOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18045,195 +11791,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorLinearShader { +function Send-OBSPauseRecord { -[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] +[Alias('obs.powershell.websocket.PauseRecord')] param( -# Set the Fill of OBSFillColorLinearShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Fill_Direction of OBSFillColorLinearShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill_Color of OBSFillColorLinearShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_linear' -$ShaderNoun = 'OBSFillColorLinearShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.005; ->; -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Top"; - int option_3_value = 3; - string option_3_label = "Bottom"; -> = 0; -uniform float4 Fill_Color; -float4 mainImage(VertData v_in) : TARGET -{ - bool is_inside_fill = true; - - // Check if the pixel is within the specified "fill width" on the left side - if(Fill_Direction == 0){ - is_inside_fill = v_in.uv.x > Fill; - } - if(Fill_Direction == 1) - { - is_inside_fill = v_in.uv.x < (1.0 - Fill); - } - if(Fill_Direction == 2) - { - is_inside_fill = v_in.uv.y > Fill; - } - if(Fill_Direction == 3) - { - is_inside_fill = v_in.uv.y < (1.0 - Fill); - } - - // Invert is_inside_fill - is_inside_fill = !is_inside_fill; - - // If inside the "fill," make the pixel selected colour; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18242,249 +11893,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialDegreesShader { +function Send-OBSPressInputPropertiesButton { -[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] +[Alias('obs.powershell.websocket.PressInputPropertiesButton')] param( -# Set the Fill_Direction of OBSFillColorRadialDegreesShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialDegreesShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialDegreesShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] -[Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialDegreesShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialDegreesShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialDegreesShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_radial_degrees' -$ShaderNoun = 'OBSFillColorRadialDegreesShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; - -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 360; - float step = 1.00000; ->; - -uniform float Start_Angle< - string label = "Start Angle"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; - -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float Offset_Y< - string label = "Offset Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float4 Fill_Color; - -float4 mainImage(VertData v_in) : TARGET -{ - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); - - // Convert angle from radians to degrees - angle = degrees(angle); - - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; - } - - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; - - // Check if the angle is within the specified "fill width" - bool is_inside_fill = angle < Fill; - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18493,252 +12010,219 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialPercentageShader { +function Send-OBSSleep { -[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] +[Alias('obs.powershell.websocket.Sleep')] param( -# Set the Fill_Direction of OBSFillColorRadialPercentageShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialPercentageShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialPercentageShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] -[Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialPercentageShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialPercentageShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialPercentageShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sleepMillis')] +[ValidateRange(0,50000)] +[double] +$SleepMillis, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sleepFrames')] +[ValidateRange(0,10000)] +[double] +$SleepFrames, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_radial_percentage' -$ShaderNoun = 'OBSFillColorRadialPercentageShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.00005; -> = 0.0; -uniform float Start_Angle< - string label = "Start Angle"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float Offset_Y< - string label = "Offset Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float4 Fill_Color; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float4 mainImage(VertData v_in) : TARGET -{ - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); +} - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); +} - // Convert angle from radians to degrees - angle = degrees(angle); + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSStreamCaption { - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] +[Alias('obs.powershell.websocket.SendStreamCaption')] +param( - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('captionText')] +[string] +$CaptionText, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // Calculate the percentage of the angle - float anglePercentage = angle / 360.0; - // Check if the angle percentage is within the specified "fill percentage" - bool is_inside_fill = anglePercentage < Fill; +process { - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18747,259 +12231,242 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFilterTemplateShader { +function Send-OBSTriggerHotkeyByKeySequence { -[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] +[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] param( -# Set the ViewProj of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSFilterTemplateShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFilterTemplateShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFilterTemplateShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFilterTemplateShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFilterTemplateShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFilterTemplateShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSFilterTemplateShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFilterTemplateShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFilterTemplateShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the notes of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('keyId')] +[string] +$KeyId, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('keyModifiers')] +[PSObject] +$KeyModifiers, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] +[switch] +$KeyModifiersshift, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.control')] +[switch] +$KeyModifierscontrol, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] +[switch] +$KeyModifiersalt, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.command')] +[switch] +$KeyModifierscommand, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'filter_template' -$ShaderNoun = 'OBSFilterTemplateShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//My shader modified by Me for use with obs-shaderfilter month/year v.02 - -//Section to converting GLSL to HLSL - can delete if unneeded -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float4x4 ViewProj; -uniform texture2d image; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ -uniform string notes< - string widget_type = "info"; -> = "add notes here"; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); } -/* -**Shaders use the built in Draw technique** -**this section can be deleted** -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -*/ +} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSTriggerHotkeyByName { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] +[Alias('obs.powershell.websocket.TriggerHotkeyByName')] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('hotkeyName')] +[string] +$HotkeyName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('contextName')] +[string] +$ContextName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -19008,699 +12475,436 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFire3Shader { +function Send-OBSTriggerMediaInputAction { -[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] +[Alias('obs.powershell.websocket.TriggerMediaInputAction')] param( -# Set the ViewProj of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSFire3Shader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFire3Shader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFire3Shader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFire3Shader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFire3Shader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFire3Shader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSFire3Shader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFire3Shader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFire3Shader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the Movement_Direction_Horizontal of OBSFire3Shader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] -[Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSFire3Shader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] -[Single] -$MovementDirectionVertical, -# Set the Alpha_Percentage of OBSFire3Shader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] -$Speed, -# Set the Invert of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFire3Shader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFire3Shader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSFire3Shader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSFire3Shader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Full_Width of OBSFire3Shader -[Alias('Full_Width')] -[ComponentModel.DefaultBindingProperty('Full_Width')] -[Management.Automation.SwitchParameter] -$FullWidth, -# Set the Flame_Size of OBSFire3Shader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Single] -$FlameSize, -# Set the Spark_Grid_Height of OBSFire3Shader -[Alias('Spark_Grid_Height')] -[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] -[Single] -$SparkGridHeight, -# Set the Flame_Modifier of OBSFire3Shader -[Alias('Flame_Modifier')] -[ComponentModel.DefaultBindingProperty('Flame_Modifier')] -[Single] -$FlameModifier, -# Set the Flame_Tongue_Size of OBSFire3Shader -[Alias('Flame_Tongue_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] -[Single] -$FlameTongueSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaAction')] +[string] +$MediaAction, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fire-3' -$ShaderNoun = 'OBSFire3Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//My effect modified by Me for use with obs-shaderfilter month/year v.02 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) - -uniform int Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 80; -uniform bool Invert = false; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.04; -uniform bool Apply_To_Image = true; -uniform bool Replace_Image_Color = true; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color = false; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float2 uv = v_in.uv; - if(Invert) - uv = 1.0 - v_in.uv; - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; } -int2 iMouse() -{ - return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); -} +} -float mod(float x, float y) -{ - return x - y * floor(x / y); -} + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSTriggerStudioModeTransition { -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x / y); -} -/*ps start*/ -#define PI 3.1415926535897932384626433832795 -uniform bool Full_Width = false; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] +[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform float Flame_Size< - string label = "Flame Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Spark_Grid_Height< - string label = "Spark Grid Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; +process { -uniform float Flame_Modifier< - string label = "Flame Modifier"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float Flame_Tongue_Size< - string label = "Flame Tongue Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 8.5; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -vec3 mod2893(vec3 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -vec4 mod289(vec4 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -vec4 permute(vec4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -vec4 taylorInvSqrt(vec4 r) -{ - return 1.79284291400159 - 0.85373472095314 * r; } -float snoise(vec3 v) -{ - const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); -// First corner - vec3 i = floor(v + dot(v, C.yyy)); - vec3 x0 = v - i + dot(i, C.xxx); +} -// Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min(g.xyz, l.zxy); - vec3 i2 = max(g.xyz, l.zxy); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentPreviewScene { - // x0 = x0 - 0.0 + 0.0 * C.xxx; - // x1 = x0 - i1 + 1.0 * C.xxx; - // x2 = x0 - i2 + 2.0 * C.xxx; - // x3 = x0 - 1.0 + 3.0 * C.xxx; - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y -// Permutations - i = mod2893(i); - vec4 p = permute(permute(permute( - i.z + vec4(0.0, i1.z, i2.z, 1.0)) - + i.y + vec4(0.0, i1.y, i2.y, 1.0)) - + i.x + vec4(0.0, i1.x, i2.x, 1.0)); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] +param( -// Gradients: 7x7 points over a square, mapped onto an octahedron. -// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) - vec4 x = x_ * ns.x + ns.yyyy; - vec4 y = y_ * ns.x + ns.yyyy; - vec4 h = 1.0 - abs(x) - abs(y); +process { - vec4 b0 = vec4(x.xy, y.xy); - vec4 b1 = vec4(x.zw, y.zw); - //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; - //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; - vec4 s0 = floor(b0) * 2.0 + 1.0; - vec4 s1 = floor(b1) * 2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; - vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - vec3 p0 = vec3(a0.xy, h.x); - vec3 p1 = vec3(a0.zw, h.y); - vec3 p2 = vec3(a1.xy, h.z); - vec3 p3 = vec3(a1.zw, h.w); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -//Normalise gradients - //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); - vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); - p0 *= norm.x; - p1 *= norm.y; - p2 *= norm.z; - p3 *= norm.w; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -// Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); - m = m * m; - return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); } -////////////////////////////////////////////////////////////// -// PRNG -// From https://www.shadertoy.com/view/4djSRW -float prng(in vec2 seed) -{ - seed = fract(seed * vec2(5.3983, 5.4427)); - seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); - return fract(seed.x * seed.y * 95.4337); -} +} -////////////////////////////////////////////////////////////// + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentProfile { -float noiseStack(vec3 pos, int octaves, float falloff) -{ - float noise = snoise(vec3(pos)); - float off = 1.0; - if (octaves > 1) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 2) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 3) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - return (1.0 + noise) / 2.0; -} -vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) -{ - float displaceA = noiseStack(pos, octaves, falloff); - float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); - return vec2(displaceA, displaceB); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] +[Alias('obs.powershell.websocket.SetCurrentProfile')] +param( -float4 mainImage(VertData v_in) : TARGET -{ - float2 UV = (1.0 - v_in.uv) * uv_scale; - if (Invert) - UV = v_in.uv * uv_scale; - float alpha = saturate(Alpha_Percentage * .01); - float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); - - vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); - if (Full_Width) - { - resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; - - } - resolution.x = mul(resolution.x, 1 / 1); - float time = iTime * (Speed * 0.01); - //vec2 drag = iMouse().xy; - vec2 offset = iMouse().xy; - // - float xpart = UV.x / resolution.x; - float ypart = UV.y / resolution.y; - // - - float ypartClip = UV.y / ( flame_size * 75.0); - float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); - float ypartClipped = min(ypartClip, 1.0); - float ypartClippedn = (1 - ypartClipped); - // - float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); - // - float timeSpeed = 0.5 * (Speed * 0.01); - float realTime = -1.0 * timeSpeed * time; - // - vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; - vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); - vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); - vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; - // - vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); - vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); - // - vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; - float noise = noiseStack(noiseCoord, 3, 0.4); - // - float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); - // - float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); - float fff = f * f * f; - vec3 fire = 1.5 * vec3(f, fff, fff * fff); - // - // smoke - float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; - float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); - vec3 smoke = vec3(smokePart, smokePart, smokePart); - // - // sparks - float sparkGridSize = Spark_Grid_Height; - vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); - sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); - sparkCoord += 100.0 * flow.xy; - if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) - sparkCoord.x += 0.5 * sparkGridSize; - vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); - float sparkRandom = prng( sparkGridIndex); - float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); - vec3 sparks = vec3(0.0, 0.0, 0.0); - if (sparkLife > 0.0) - { - float sparkSize = xfuel * xfuel * sparkRandom * 0.08; - float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; - vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); - vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; - vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); - float sparkLength = length(sparkModulus); - float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); - sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); - } - // - float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire, 0.0, alpha); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); - } - else - { - rgba = color; - } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - - return rgba; -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +process { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -19709,434 +12913,432 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireShader { +function Set-OBSCurrentProgramScene { -[Alias('Set-OBSFireShader','Add-OBSFireShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] +[Alias('obs.powershell.websocket.SetCurrentProgramScene')] param( -# Set the Alpha_Percentage of OBSFireShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFireShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] -$Speed, -# Set the Flame_Size of OBSFireShader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Int32] -$FlameSize, -# Set the Fire_Type of OBSFireShader -[Alias('Fire_Type')] -[ComponentModel.DefaultBindingProperty('Fire_Type')] -[Int32] -$FireType, -# Set the Invert of OBSFireShader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFireShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFireShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSFireShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSFireShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Notes of OBSFireShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fire' -$ShaderNoun = 'OBSFireShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 -// https://github.com/Oncorporation/obs-shaderfilter plugin -// https://www.shadertoy.com/view/MtcGD7 original version -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -//v.5 -// flicker -// flame type -// apply to image -// replace image color -// speed -// flame size -// alpha -// invert direction/position -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int Alpha_Percentage< - string label = "Aplha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 100; -uniform int Flame_Size< - string label = "Flame Size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 70; -uniform int Fire_Type< - string label = "Fire Type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Smaller and more whisps"; - int option_1_value = 1; - string option_1_label = "Larger and more volume"; -> = 1; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform bool Invert < - string name = "Invert"; -> = false; -uniform float lumaMin< - string label = "Luma min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma min smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.04; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string Notes< - string widget_type = "info"; -> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -vec3 rgb2hsv(vec3 c) -{ - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -vec3 hsv2rgb(vec3 c) -{ - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -float rand(vec2 n) -{ - return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); - //return sin(rand_f, n); -} -float noise(vec2 n) -{ - const vec2 d = vec2(0.0, 1.0); - vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); - return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); -} +} -float fbm(vec2 n) -{ - float total = 0.0, amplitude = 1.0; - for (int i = 0; i < 5; i++) - { - total += noise(n) * amplitude; - n += n * 1.7; - amplitude *= 0.47; - } - return total; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneCollection { -float4 mainImage(VertData v_in) : TARGET -{ - float2 iResolution = uv_scale; - float flame_size = clamp(Flame_Size * .01,-5,5); - // inverting direction is logically inverted to allow the bottom up to be normal - float fire_base = (v_in.uv.y / iResolution.y); - float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); - float direction = -1.0 * clamp(Speed*.01,-5,5); - if (!Invert) - { - direction *= -1.0; - fire_base = 1 - fire_base; - fire_pix = 1 - fire_pix; - } - float iTime = direction * elapsed_time; - - const vec3 c1 = vec3(0.5, 0.0, 0.1); - const vec3 c2 = vec3(0.9, 0.1, 0.0); - const vec3 c3 = vec3(0.2, 0.1, 0.7); - const vec3 c4 = vec3(1.0, 0.9, 0.1); - const vec3 c5 = vec3(0.1, 0.1, 0.1); - const vec3 c6 = vec3(0.9, 0.9, 0.9); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] +[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] +param( - vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); - float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; - float alpha = saturate(Alpha_Percentage * .01); - - //change the constant term for all kinds of cool distance versions, - //make plus/minus to switch between - //ground fire and fire rain! - float dist = 3.5 - sin(iTime * 0.4) / 1.89; - - vec2 p = fire_pix * dist / iResolution.xx; - p.x -= iTime / 1.1; - float3 black = float3(0,0,0); - vec3 fire; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (Fire_Type == 1) - { - //fire version 1 larger and more volume - float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); - float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); - float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; - float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; - q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; - vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), - fbm(p - q - iTime* speed.y)) ; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); +process { - fire += .05; - fire.r *= .8; - vec3 hsv = rgb2hsv(fire); - hsv.y *= hsv.z * 1.1; - hsv.z *= hsv.y * 1.13; - hsv.y = (2.2 - hsv.z * .9) * 1.20; - fire = hsv2rgb(hsv); - } - else - { - // fire version 0 - smaller and more whisps - p += (rand_f *.01); - float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); - float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); - float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; - float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; - q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; - vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), - fbm(p + q - iTime * speed.y)) * shift; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } - fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); - fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); - fire = fire/(1.0+max(black,fire)); - } - float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire,0.0,alpha); + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - else - { - rgba = color; + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - return rgba; + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneTransition { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneTransitionDuration { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20145,256 +13347,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworks2Shader { +function Set-OBSCurrentSceneTransitionSettings { -[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] param( -# Set the Speed of OBSFireworks2Shader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionSettings')] +[PSObject] +$TransitionSettings, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fireworks2' -$ShaderNoun = 'OBSFireworks2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/4dBGRw - -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 1.0; -> = 100.0; - -#ifndef OPENGL -#define mat2 float2x2 -#define mix lerp -float mod(float x, float y) -{ - return x - y * floor(x / y); -} -#endif -//Creates a diagonal red-and-white striped pattern. -float3 barberpole(float2 pos, float2 rocketpos){ - float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); - float3 col=float3(1.0,1.0,1.0); - - d = mod(d*20.,2.0); - if(d>1.0){ - col=float3(1.0,0.0,0.0); - } - return col; -} - -float4 rocket(float2 pos, float2 rocketpos){ - float4 col = float4(0.0,0.0,0.0,0.0); - float f = 0.; - float absx= abs(rocketpos.x - pos.x); - float absy = abs(rocketpos.y-pos.y); - //wooden stick - if(absx<0.01&&absy<0.22){ - col=float4(1.0,0.5,0.5,1.0); - } - - //Barberpole - - if(absx<0.05&&absy<0.15){ - col=float4(barberpole(pos, rocketpos),1.0); - } - //Rocket Point - float pointw=(rocketpos.y-pos.y-0.25)*-0.7; - if((rocketpos.y-pos.y)>0.1){ - f=smoothstep(pointw-0.001,pointw+0.001,absx); - - col=mix(float4(1.0,0.0,0.0,1.0),col, f); - } - //Shadow - - f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); - col.rgb *= 0.7+f; - - return col; -} - - - -float rand(float val, float seed){ - return cos(val*sin(val*seed)*seed); -} - -float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ - float4 col= float4(0.0,0.0,0.0,0.0); - float2 pp = float2(1.0,0.0); - mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); - for(float i=1.0;i<=128.0;i++){ - float d=rand(i, seed); - float fade=(i/128.0)*time; - float2 particpos = cpos + time*pp*d; - pp = mul(rr,pp); - col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); - } - col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); - col.a = col.r+col.g+col.b; - return col; -} -float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ - - float timeoffset = 2.0; - float4 col=float4(0.0,0.0,0.0,0.0); - if(time<=0.){ - return col; - } - time *= Speed /100.0; - if(mod(time, 6.0)>timeoffset){ - col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); - }else{ - - col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); - } - return col; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; - uv.y = -uv.y; - uv.x *= uv_size.x/uv_size.y; - float4 col = image.Sample(textureSampler, v_in.uv); - //col.rgb += 0.1*uv.y; - float4 c; - c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); - col = mix(col, c, c.a); - - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20403,313 +13459,233 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworksShader { +function Set-OBSInputAudioBalance { -[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] +[Alias('obs.powershell.websocket.SetInputAudioBalance')] param( -# Set the show_flash of OBSFireworksShader -[Alias('show_flash')] -[ComponentModel.DefaultBindingProperty('show_flash')] -[Management.Automation.SwitchParameter] -$ShowFlash, -# Set the show_stars of OBSFireworksShader -[Alias('show_stars')] -[ComponentModel.DefaultBindingProperty('show_stars')] -[Management.Automation.SwitchParameter] -$ShowStars, -# Set the use_transparancy of OBSFireworksShader -[Alias('use_transparancy')] -[ComponentModel.DefaultBindingProperty('use_transparancy')] -[Management.Automation.SwitchParameter] -$UseTransparancy, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioBalance')] +[ValidateRange(0,1)] +[double] +$InputAudioBalance, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fireworks' -$ShaderNoun = 'OBSFireworksShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif -uniform bool show_flash = true; -uniform bool show_stars = true; -uniform bool use_transparancy = true; -float distLine(float2 p, float2 a, float2 b) { - float2 pa = p - a; - float2 ba = b - a; - float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * t); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float linef(float2 uv, float2 a, float2 b, float w) { - //return smoothstep(w, w - 0.01, distLine(uv, a, b)); - return w / distLine(uv, a, b); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float N21(float2 p) { - p = fract(p * float2(233.34, 851.73)); - p += dot(p, p + 23.45); - return fract(p.x * p.y); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float2 N22(float2 p) { - float n = N21(p); - return float2(n, N21(p + n)); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float N11(float n) { - return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float particle(float2 uv, float2 p, float2 v, float r, float t) { - float g = -9.81; - float x = p.x + v.x * t; - float y = p.y + v.y * t + g / 2.0 * t * t; - float2 j = (float2(x, y) - uv) * 20.0; - float sparkle = 1.0 / dot(j, j); - return sparkle; } -float2 p1(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); -} -float2 p2(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); -} +} -float endTime(float h) { - return pow(h, 1.0 / 5.0) * 1.1; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioMonitorType { -float explosion(float2 uv, float2 p, float s, float n, float f, float t) { - float m = 0.0; - float dt = 0.5; - float seed2 = 0.32; - for(float i = 0.0; i < n; i++) { - seed2 += i; - float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); - float2 v = float2(cos(seed2), sin(seed2)) + rand; - m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); - } - return m; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] +param( -float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { - float2 p1v = p1(p, h, t); - float e = endTime(h); - return explosion(uv, p1v, s, n, f, t - e * 0.9); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, -float shaft(float2 uv, float2 p, float w, float h, float t) { - float2 p1v = p1(p, h, t) + float2(0.0, 0.3); - float2 p2v = p2(p, h, t); - float e = 1.0 / 0.95 * endTime(h); - float2 j = (p1v - uv) * 15.0; - float sparkle = 1.0 / dot(j, j); - return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, -float3 base(float2 uv) { - return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorType')] +[string] +$MonitorType, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float back(float2 uv, float2 p, float t) { - float dt = 0.3; - float j = length(p - uv); - float m = exp(-0.005 * j * j); - return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); -} -float stars(float2 uv) { - float r = N21(uv); - return smoothstep(0.001, 0.0, r); -} +process { -float mod(float x, float y) -{ - return x - y * floor(x / y); -} -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv - float2(0.5,0.5); - uv.y = uv.y * -1; - float t = elapsed_time / 10.0; - float scale = 10.0; - uv *= scale; - // - float4 col = image.Sample(textureSampler, v_in.uv); - if(show_stars){ - float c = stars(uv); - if(use_transparancy){ - col += float4(c,c,c,c)*(1.0-col.a); - }else{ - col += float4(c,c,c,c);//*(1.0-orig_col.a); - } - - } - - float a = -0.035 * sin(t * 15.0); - float co = cos(a); - float si = sin(a); - mat2 trans1 = mat2(float2(co, si), float2(-si, co)); - float2 trans2 = float2(-15.0 * a, 0.0); -#ifndef OPENGL - uv = mul(uv, trans1); -#else - uv *= trans1; -#endif - uv += trans2; - - for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { - float ti = mod(t * 9.0 - i * 5.0, 4.0); - float scale = mix(2.0, 0.3, ti / 4.0); - float2 uvs = uv * scale; - float rand = N11(i); - float h = 10.0 + rand * 4.0; - float w = 0.02; - float n = 80.0; - float s = 0.9; - float f = 1.5; - float2 p = float2(mix(-8.0, 8.0, rand), -10.0); - float fw = fireworks(uvs, p, h, n, s, f, ti); - float3 bc = base(uv); - col += float4(bc*fw, fw); - col += shaft(uvs, p, w, h, ti); - if(show_flash){ - if(use_transparancy){ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; - }else{ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8); - } - } - } - - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20718,188 +13694,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeShader { +function Set-OBSInputAudioSyncOffset { -[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] param( -# Set the center_x_percent of OBSFisheyeShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Single] -$CenterYPercent, -# Set the power of OBSFisheyeShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] +[ValidateRange(-950,20000)] +[double] +$InputAudioSyncOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fisheye' -$ShaderNoun = 'OBSFisheyeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); - } else if(power <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); - } - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20908,213 +13812,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeXyShader { +function Set-OBSInputAudioTracks { -[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] +[Alias('obs.powershell.websocket.SetInputAudioTracks')] param( -# Set the center_x_percent of OBSFisheyeXyShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeXyShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Single] -$CenterYPercent, -# Set the power_x of OBSFisheyeXyShader -[Alias('power_x')] -[ComponentModel.DefaultBindingProperty('power_x')] -[Single] -$PowerX, -# Set the power_y of OBSFisheyeXyShader -[Alias('power_y')] -[ComponentModel.DefaultBindingProperty('power_y')] -[Single] -$PowerY, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, -process { -$shaderName = 'fisheye-xy' -$ShaderNoun = 'OBSFisheyeXyShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float power_x< - string label = "Power x"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; -uniform float power_y< - string label = "Power y"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioTracks')] +[PSObject] +$InputAudioTracks, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power_x >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; - } else if(power_x <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; - } - if (power_y >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; - } else if(power_y <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; - } - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +process { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21123,163 +13929,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFlipShader { +function Set-OBSInputMute { -[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] +[Alias('obs.powershell.websocket.SetInputMute')] param( -# Set the Horizontal of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputMuted')] +[switch] +$InputMuted, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Flip' -$ShaderNoun = 'OBSFlipShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// A Simple Flip Shader - -uniform bool Horizontal< - string label = "Flip horizontally"; -> = true; -uniform bool Vertical< - string label = "Flip vertically"; -> = true; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - if (Horizontal == true) { - pos.x = 1 - pos.x; - } - if (Vertical == true) { - pos.y = 1 - pos.y; - } - - return image.Sample(textureSampler, pos); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21288,238 +14046,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFrostedGlassShader { +function Set-OBSInputName { -[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] +[Alias('obs.powershell.websocket.SetInputName')] param( -# Set the Alpha_Percent of OBSFrostedGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Amount of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Amount')] -[Single] -$Amount, -# Set the Scale of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Scale')] -[Single] -$Scale, -# Set the Animate of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the Horizontal_Border of OBSFrostedGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSFrostedGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] -[Single] -$BorderOffset, -# Set the Border_Color of OBSFrostedGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the notes of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, -process { -$shaderName = 'frosted_glass' -$ShaderNoun = 'OBSFrostedGlassShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter - -uniform float Alpha_Percent< - string label = "Alpha Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Amount< - string label = "Amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.03; -uniform float Scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 5.1; -uniform bool Animate; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.1; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform string notes< - string widget_type = "info"; -> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newInputName')] +[string] +$NewInputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float rand(float2 co) -{ - float scale = Scale; - if (Animate) - scale *= rand_f; - float2 v1 = float2(92.0,80.0); - float2 v2 = float2(41.0,62.0); - return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); -} -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - float3 tc = rgba.rgb * Border_Color.rgb; - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; +process { - if (uv_compare < (Border_Offset - 0.005)) - { - float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); - tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - tc = image.Sample(textureSampler, v_in.uv).rgb; - } - return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21528,184 +14163,120 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGammaCorrectionShader { +function Set-OBSInputSettings { -[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] +[Alias('obs.powershell.websocket.SetInputSettings')] param( -# Set the Red of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Red')] -[Single] -$Red, -# Set the Green of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Green')] -[Single] -$Green, -# Set the Blue of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Blue')] -[Single] -$Blue, -# Set the notes of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gamma_correction' -$ShaderNoun = 'OBSGammaCorrectionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -uniform float Red< - string label = "Red"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform float Green< - string label = "Green"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform float Blue< - string label = "Blue"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform string notes< - string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction." -float4 mainImage(VertData v_in) : TARGET -{ - float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); - float4 c = image.Sample(textureSampler, v_in.uv); - c.rgb = pow(c.rgb, 1.0 / gammaRGB); - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21714,264 +14285,122 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurAdvancedShader { +function Set-OBSInputVolume { -[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] +[Alias('obs.powershell.websocket.SetInputVolume')] param( -# Set the Directions of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Directions')] -[Single] -$Directions, -# Set the Quality of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Quality')] -[Single] -$Quality, -# Set the Size of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Size')] -[Single] -$Size, -# Set the Mask_Left of OBSGaussianBlurAdvancedShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurAdvancedShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurAdvancedShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeMul')] +[ValidateRange(0,20)] +[double] +$InputVolumeMul, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeDb')] +[ValidateRange(-100,26)] +[double] +$InputVolumeDb, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur-advanced' -$ShaderNoun = 'OBSGaussianBlurAdvancedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Directions< - string label = "Directions (16.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) -uniform float Quality< - string label = "Quality (4.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) -uniform float Size< - string label = "Size (8.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 8.0; // BLUR SIZE (Radius) -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; - -float4 mainImage(VertData v_in) : TARGET -{ - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; - - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; - c.a = transparent / count; - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21980,342 +14409,228 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurShader { +function Set-OBSMediaInputCursor { -[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] +[Alias('obs.powershell.websocket.SetMediaInputCursor')] param( -# Set the ViewProj of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the imageSize of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageSize')] -[Single[]] -$ImageSize, -# Set the imageTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageTexel')] -[Single[]] -$ImageTexel, -# Set the u_radius of OBSGaussianBlurShader -[Alias('u_radius')] -[ComponentModel.DefaultBindingProperty('u_radius')] -[Int32] -$URadius, -# Set the u_diameter of OBSGaussianBlurShader -[Alias('u_diameter')] -[ComponentModel.DefaultBindingProperty('u_diameter')] -[Int32] -$UDiameter, -# Set the u_texelDelta of OBSGaussianBlurShader -[Alias('u_texelDelta')] -[ComponentModel.DefaultBindingProperty('u_texelDelta')] -[Single[]] -$UTexelDelta, -# Set the elapsed_time of OBSGaussianBlurShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianBlurShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianBlurShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianBlurShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the kernel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernel')] -[String] -$Kernel, -# Set the kernelTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernelTexel')] -[Single[]] -$KernelTexel, -# Set the pixel_size of OBSGaussianBlurShader -[Alias('pixel_size')] -[ComponentModel.DefaultBindingProperty('pixel_size')] -[Single] -$PixelSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursor')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$MediaCursor, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur' -$ShaderNoun = 'OBSGaussianBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -// OBS Default -uniform float4x4 ViewProj; -// Settings (Shared) -uniform texture2d image; -uniform float2 imageSize; -uniform float2 imageTexel; -uniform int u_radius; -uniform int u_diameter; -uniform float2 u_texelDelta; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// Settings (Private) -//uniform float registerkernel[25]; -uniform texture2d kernel; -uniform float2 kernelTexel; -uniform float pixel_size = 1.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state pointClampSampler { - Filter = Point; - AddressU = Clamp; - AddressV = Clamp; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -sampler_state bilinearClampSampler { - Filter = Bilinear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float Gaussian(float x, float o) -{ - float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); } -VertData VSDefault(VertData vert_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = vert_in.uv; - return vert_out; -} -float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel) - { - float l_gauss = Gaussian(0.0, 1.0); - float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_gauss += l_g; - } - l_value = l_value * (1.0 / l_gauss); - return l_value; -} +} -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSOutputSettings { -/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - if (pixel_size % 2 == 0) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = p_texel; - float2 l_koffset = p_kernelTexel; - for (int k = 1; k <= pixel_size; k++) { - float l_g = kernel.Sample(pointClampSampler, l_koffset).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_uvoffset += p_texel; - l_koffset += p_kernelTexel; - } - return l_value; - } else { - return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) - } -}*/ -float4 PSGaussian(VertData vert_in) : TARGET -{ - - float4 color = image.Sample(pointClampSampler, vert_in.uv); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] +[Alias('obs.powershell.websocket.SetOutputSettings')] +param( - float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, - return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputSettings')] +[PSObject] +$OutputSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - /* - return InternalGaussianPrecalculated( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ - /* - return InternalGaussianPrecalculatedNVOptimize( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ -} +process { -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSGaussian(vert_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22324,249 +14639,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurSimpleShader { +function Set-OBSPersistentData { -[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] +[Alias('obs.powershell.websocket.SetPersistentData')] param( -# Set the Strength of OBSGaussianBlurSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSGaussianBlurSimpleShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurSimpleShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurSimpleShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurSimpleShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotValue')] +[PSObject] +$SlotValue, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur-simple' -$ShaderNoun = 'OBSGaussianBlurSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1.0; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - - float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) - float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) - - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; - - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - c.a = transparent / count; - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22575,378 +14756,345 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianExampleShader { +function Set-OBSProfileParameter { -[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] +[Alias('obs.powershell.websocket.SetProfileParameter')] param( -# Set the ViewProj of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianExampleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianExampleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianExampleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSGaussianExampleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSGaussianExampleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the initial_image of OBSGaussianExampleShader -[Alias('initial_image')] -[ComponentModel.DefaultBindingProperty('initial_image')] -[String] -$InitialImage, -# Set the before_image of OBSGaussianExampleShader -[Alias('before_image')] -[ComponentModel.DefaultBindingProperty('before_image')] -[String] -$BeforeImage, -# Set the after_image of OBSGaussianExampleShader -[Alias('after_image')] -[ComponentModel.DefaultBindingProperty('after_image')] -[String] -$AfterImage, -# Set the text_color of OBSGaussianExampleShader -[Alias('text_color')] -[ComponentModel.DefaultBindingProperty('text_color')] -[String] -$TextColor, -# Set the max_distance of OBSGaussianExampleShader -[Alias('max_distance')] -[ComponentModel.DefaultBindingProperty('max_distance')] -[Single] -$MaxDistance, -# Set the exp of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('exp')] -[Single] -$Exp, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterValue')] +[string] +$ParameterValue, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-example' -$ShaderNoun = 'OBSGaussianExampleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -/*-------------------------. -| :: Texture and sampler:: | -''-------------------------*/ + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform texture2d initial_image; -sampler_state initial_sampler -{ - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = initial_image; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform texture2d before_image; -sampler_state before_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = before_image; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform texture2d after_image; -sampler_state after_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = after_image; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; +} -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -struct ColorData { - float4 initial_color : SV_TARGET0; - float4 before_color: SV_TARGET1; - float4 after_color : SV_TARGET2; -}; +} -uniform float4 text_color; -uniform float max_distance; -uniform float exp; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSRecordDirectory { -#define PI 3.141592653589793238462643383279502884197169399375105820974 -VertData mainTransform(VertData v_in) -{ - VertData vert_out = v_in; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] +[Alias('obs.powershell.websocket.SetRecordDirectory')] +param( -float4 grayscale(float4 color) -{ - float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; - return float4(grayscale, grayscale, grayscale, color.a); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('recordDirectory')] +[string] +$RecordDirectory, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float4 gaussian(VertData v_in, float angle) -{ - float rad = radians(angle); - float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); - float2 dir_2 = dir * 2.0; - float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; - - float4 px_away = image.Sample(textureSampler, v_in.uv + dir); - px_away += image.Sample(textureSampler, v_in.uv - dir); - px_away *= 0.25; - - float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away *= 0.0625; - - return ret + px_away + px_2_away; -} -ColorData setColorData(VertData v_in): SV_TARGET0 -{ - //string RenderTarget0 = "initial_image"; - ColorData cd;// = (ColorData)0; - cd.initial_color = image.Sample(textureSampler, v_in.uv); - cd.before_color = float4(0.0,0.0,1.0,1.0); - cd.after_color = float4(1.0,0.0,0.0,1.0); - return cd; -} +process { -float4 blurImageH(VertData v_in) : SV_TARGET1 -{ - //string RenderTarget1 = "before_image"; - //ColorData cd = (ColorData)0; - //cd.initial_color = image.Sample(textureSampler, v_in.uv); - //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); - return float4(0.0,0.0,1.0,1.0); -} -float4 blurImageV(VertData v_in) : SV_TARGET2 -{ - //string RenderTarget2 = "after_image"; - //ColorData cd = (ColorData)0; - //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); - return float4(1.0,0.0,0.0,1.0); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : SV_TARGET0 -{ - float4 color; - ColorData cd;// = (ColorData)0; - - //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); - //cd.before_color = before_image.Sample(before_sampler, v_in.uv); - cd.after_color = after_image.Sample(before_sampler, v_in.uv); - - if (max_distance <= 5) { - color = cd.before_color; - } - else { - color = cd.after_color;//image.Sample(textureSampler, v_in.uv); - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float4 gray = grayscale(color); - float4 gray_text = grayscale(text_color); - float d = distance(gray.rgb, gray_text.rgb); - if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ - float d_c = pow(d*2, exp) / pow(2, exp); - d_c = sin(d_c * PI / 2); - d = pow(1.0 - sin(d * PI/4), exp); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - color.rgb = float3(d,d,d); - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return color; } -technique Draw -{ - pass pre - { - vertex_shader = mainTransform(v_in); - pixel_shader = setColorData(v_in); - } - pass b0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageH(v_in); - } +} - pass b1 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageV(v_in); - } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemBlendMode { - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] +param( -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] +[string] +$SceneItemBlendMode, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22955,290 +15103,245 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianSimpleShader { +function Set-OBSSceneItemEnabled { -[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] +[Alias('obs.powershell.websocket.SetSceneItemEnabled')] param( -# Set the ViewProj of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianSimpleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianSimpleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianSimpleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianSimpleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSGaussianSimpleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSGaussianSimpleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSGaussianSimpleShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSGaussianSimpleShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSGaussianSimpleShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the samples of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('samples')] -[Int32] -$Samples, -# Set the LOD of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('LOD')] -[Int32] -$LOD, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-simple' -$ShaderNoun = 'OBSGaussianSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/ltScRG Converted inspiration - -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float - -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** - -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ -// 16x acceleration of https://www.shadertoy.com/view/4tSyzy -// by applying gaussian at intermediate MIPmap level. + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int samples< - string label = "Samples"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 25; - int step = 1; -> = 16; -uniform int LOD< - string label = "LOD"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 2; // gaussian done on MIPmap at scale LOD + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float gaussian(vec2 i) -{ - float sigma = (float(samples) * .25); - return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -vec4 blur(vec2 U, vec2 scale) -{ - vec4 O = vec4(0,0,0,0); - int sLOD = (1 << LOD); // tile size = 2^LOD - int s = samples / sLOD; - - for (int i = 0; i < s * s; i++) - { - vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; - O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); - //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); - //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - return O / O.a; -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; - //float2 iResolution = 1 - v_in.uv + 1.0; - //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); - return blur(v_in.uv / iResolution, 1.0 / iResolution); - //return rgba; } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemIndex { +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] +[Alias('obs.powershell.websocket.SetSceneItemIndex')] +param( -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -23247,335 +15350,121 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGbCameraShader { +function Set-OBSSceneItemLocked { -[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] +[Alias('obs.powershell.websocket.SetSceneItemLocked')] param( -# Set the pixelSize of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('pixelSize')] -[Single] -$PixelSize, -# Set the dither_factor of OBSGbCameraShader -[Alias('dither_factor')] -[ComponentModel.DefaultBindingProperty('dither_factor')] -[Single] -$DitherFactor, -# Set the alternative_bayer of OBSGbCameraShader -[Alias('alternative_bayer')] -[ComponentModel.DefaultBindingProperty('alternative_bayer')] -[Management.Automation.SwitchParameter] -$AlternativeBayer, -# Set the brightness of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('brightness')] -[Single] -$Brightness, -# Set the contrast of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('contrast')] -[Single] -$Contrast, -# Set the gamma of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('gamma')] -[Single] -$Gamma, -# Set the color_1 of OBSGbCameraShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the color_2 of OBSGbCameraShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the color_3 of OBSGbCameraShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the color_4 of OBSGbCameraShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] -[String] -$Color4, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemLocked')] +[switch] +$SceneItemLocked, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gb-camera' -$ShaderNoun = 'OBSGbCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * ------------------------------------------------------------ - * "THE BEERWARE LICENSE" (Revision 42): - * maple wrote this code. As long as you retain this - * notice, you can do whatever you want with this stuff. If we - * meet someday, and you think this stuff is worth it, you can - * buy me a beer in return. - * ------------------------------------------------------------ - * from https://www.shadertoy.com/view/3tSXRh - * adopted for OBS by Exeldro - * ------------------------------------------------------------ - */ - -uniform float pixelSize< - string label = "Pixel Size"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 50.0; - float step = 0.1; -> = 3.0; - -uniform float dither_factor< - string label = "Dither Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; - -uniform bool alternative_bayer; - -uniform float brightness< - string label = "Brightness"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float contrast< - string label = "Contrast"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float gamma< - string label = "Gamma"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.6; - -uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; -uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; -uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; -uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; - -// quantize coords to low resolution -float2 pixelize(float2 uv, float2 pixelSize) { - float2 factor = pixelSize / uv_size; - return floor(uv / factor) * factor; -} - -float3 colorLUT(float3 color) { - float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; - if(gray < 0.25) - return color_1.rgb; - if(gray < 0.50) - return color_2.rgb; - if(gray < 0.75) - return color_3.rgb; - return color_4.rgb; -} - -// adjust brightness, contrast and gamma levels of a color -float3 levels(float3 color, float brightness, float contrast, float3 gamma) { - float3 value = (color - 0.5) * contrast + 0.5; - value = clamp(value + brightness, 0.0, 1.0); - return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); -} -float3 levels(float3 color, float brightness, float contrast, float gamma) { - return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); -} - -// applies the dithering filter to a color map -float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { - // reduces pixel space to the selected pixel size - float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); - - // applies the bayer matrix filter to the color map - pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); - int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); - float bayer; - if (alternative_bayer){ -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ - 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ - 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ - 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ - 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ - 51, 19, 59, 27, 49, 17, 57, 25, - 15, 47, 7, 39, 13, 45, 5, 37, - 63, 31, 55, 23, 61, 29, 53, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; - } else { -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 48, 12, 60, 3, 51, 15, 63, - 32, 16, 44, 28, 35, 19, 47, 31, - 8, 56, 4, 52, 11, 59, 7, 55, - 40, 24, 36, 20, 43, 27, 39, 23, - 2, 50, 14, 62, 1, 49, 13, 61, - 34, 18, 46, 30, 33, 17, 45, 29, - 10, 58, 6, 54, 9, 57, 5, 53, - 42, 26, 38, 22, 41, 25, 37, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; - } - float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); - // limits it to the selected palette - color = colorLUT(bayerColor); - - return color; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); - texcoord = clamp(texcoord, 0.001, 1.0); - float4 c = image.Sample(textureSampler, texcoord); - float3 color = c.rgb; - - color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); - - color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); - - return float4(color.r, color.g, color.b, c.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -23584,267 +15473,238 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlassShader { +function Set-OBSSceneItemTransform { -[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] +[Alias('obs.powershell.websocket.SetSceneItemTransform')] param( -# Set the Alpha_Percent of OBSGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Offset_Amount of OBSGlassShader -[Alias('Offset_Amount')] -[ComponentModel.DefaultBindingProperty('Offset_Amount')] -[Single] -$OffsetAmount, -# Set the xSize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('xSize')] -[Int32] -$XSize, -# Set the ySize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('ySize')] -[Int32] -$YSize, -# Set the Reflection_Offset of OBSGlassShader -[Alias('Reflection_Offset')] -[ComponentModel.DefaultBindingProperty('Reflection_Offset')] -[Int32] -$ReflectionOffset, -# Set the Horizontal_Border of OBSGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] -[Single] -$BorderOffset, -# Set the Border_Color of OBSGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the Glass_Color of OBSGlassShader -[Alias('Glass_Color')] -[ComponentModel.DefaultBindingProperty('Glass_Color')] -[String] -$GlassColor, -# Set the notes of OBSGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemTransform')] +[PSObject] +$SceneItemTransform, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glass' -$ShaderNoun = 'OBSGlassShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 -uniform float Alpha_Percent< - string label = "Alpha Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Offset_Amount< - string label = "Offset Amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; -uniform int xSize< - string label = "x Size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int ySize< - string label = "y Size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int Reflection_Offset< - string label = "Reflection Offset"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 2; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; - string widget_type = "slider"; - float minimum = -0.01; - float maximum = 1.01; - float step = 0.01; -> = 0.5; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform float4 Glass_Color; -uniform string notes< - string widget_type = "info"; -> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; - -float mod(float a, float b){ - float d = a / b; - return (d-floor(d))*b; -} - -float4 mainImage(VertData v_in) : TARGET -{ - - int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); - int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); - float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); - float2 uv = v_in.uv + offsets; - float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); - float4 rgba = image.Sample(textureSampler, v_in.uv); - float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); - rgba = image.Sample(textureSampler, uv); - float4 rgba_glass = image.Sample(textureSampler, uv2); - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (uv_compare < (Border_Offset - 0.005)) - { - rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - rgba_output = image.Sample(textureSampler, v_in.uv); - } - return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneName { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] +[Alias('obs.powershell.websocket.SetSceneName')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newSceneName')] +[string] +$NewSceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -23853,352 +15713,243 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchAnalogShader { +function Set-OBSSceneSceneTransitionOverride { -[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] param( -# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader -[Alias('scan_line_jitter_displacement')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] -[Single] -$ScanLineJitterDisplacement, -# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader -[Alias('scan_line_jitter_threshold_percent')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] -[Int32] -$ScanLineJitterThresholdPercent, -# Set the vertical_jump_amount of OBSGlitchAnalogShader -[Alias('vertical_jump_amount')] -[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] -[Single] -$VerticalJumpAmount, -# Set the vertical_speed of OBSGlitchAnalogShader -[Alias('vertical_speed')] -[ComponentModel.DefaultBindingProperty('vertical_speed')] -[Single] -$VerticalSpeed, -# Set the horizontal_shake of OBSGlitchAnalogShader -[Alias('horizontal_shake')] -[ComponentModel.DefaultBindingProperty('horizontal_shake')] -[Single] -$HorizontalShake, -# Set the color_drift_amount of OBSGlitchAnalogShader -[Alias('color_drift_amount')] -[ComponentModel.DefaultBindingProperty('color_drift_amount')] -[Single] -$ColorDriftAmount, -# Set the color_drift_speed of OBSGlitchAnalogShader -[Alias('color_drift_speed')] -[ComponentModel.DefaultBindingProperty('color_drift_speed')] -[Single] -$ColorDriftSpeed, -# Set the pulse_speed_percent of OBSGlitchAnalogShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the alpha_percent of OBSGlitchAnalogShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] -[Int32] -$AlphaPercent, -# Set the rotate_colors of OBSGlitchAnalogShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Replace_Image_Color of OBSGlitchAnalogShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGlitchAnalogShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the notes of OBSGlitchAnalogShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glitch_analog' -$ShaderNoun = 'OBSGlitchAnalogShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float scan_line_jitter_displacement< - string label = "Scan line jitter"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 33.0; // (displacement, threshold) -uniform int scan_line_jitter_threshold_percent< - string label = "scan line jitter threshold percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 95; -uniform float vertical_jump_amount< - string label = "Vertical jump amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float vertical_speed< - string label = "Vertical speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform float horizontal_shake< - string label = "Horizontal shake"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float color_drift_amount< - string label = "Color drift amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float color_drift_speed< - string label = "Color drift speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int alpha_percent< - string label = "Aplha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string notes< - string widget_type = "info"; -> ="play with settings!"; -float nrand(float x, float y) -{ - float value = dot(float2(x, y), float2(12.9898 , 78.233 )); - return frac(sin(value) * 43758.5453); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float speed = pulse_speed_percent * 0.01; - float alpha = alpha_percent * 0.01; - float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; - float u = v_in.uv.x; - float v = v_in.uv.y; - float t = sin(elapsed_time * speed) * 2 - 1; - float4 rgba = image.Sample(textureSampler, v_in.uv); - // Scan line jitter - float jitter = nrand(v, t) * 2 - 1; - jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; +} - // Vertical jump - float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSourceFilterEnabled { - // Horizontal shake - float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; - //// Color drift - float drift = sin(jump + color_drift_speed) * color_drift_amount; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] +[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] +param( - float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); - float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); - - if(rotate_colors) - { - // get general time number between 0 and 4 - float tx = (t + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls - //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 - src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); - //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 - src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); - //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 - src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); - - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, - float4 color = rgba; - float4 original_color = color; - rgba = float4(src1.x, src2.x, src1.y, alpha); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - if (Apply_To_Alpha_Layer) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color, alpha); - } - - if (Apply_To_Specific_Color) - { - color = original_color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - - return rgba; -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterEnabled')] +[switch] +$FilterEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24207,213 +15958,121 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchShader { +function Set-OBSSourceFilterIndex { -[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] +[Alias('obs.powershell.websocket.SetSourceFilterIndex')] param( -# Set the AMT of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('AMT')] -[Single] -$AMT, -# Set the SPEED of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('SPEED')] -[Single] -$SPEED, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$FilterIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glitch' -$ShaderNoun = 'OBSGlitchShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MtXBDs -//inputs -uniform float AMT< - string label = "AMT"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.2; //0 - 1 glitch amount -uniform float SPEED< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; //0 - 1 speed - -//2D (returns 0 - 1) -float random2d(float2 n) { - return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); -} - -float randomRange (in float2 seed, in float min, in float max) { - return min + random2d(seed) * (max - min); -} - -// return 1 if v inside 1d range -float insideRange(float v, float bottom, float top) { - return step(bottom, v) - step(top, v); -} - -float4 mainImage(VertData v_in) : TARGET -{ - - float time = floor(elapsed_time * SPEED * 60.0); - float2 uv = v_in.uv; - - //copy orig - float4 outCol = image.Sample(textureSampler, uv); - - //randomly offset slices horizontally - float maxOffset = AMT/2.0; - for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { - float sliceY = random2d(float2(time , 2345.0 + float(i))); - float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; - float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); - float2 uvOff = uv; - uvOff.x += hOffset; - if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ - outCol = image.Sample(textureSampler, uvOff); - } - } - - //do slight offset on one entire channel - float maxColOffset = AMT/6.0; - float rnd = random2d(float2(time , 9545.0)); - float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), - randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); - if (rnd < 0.33){ - outCol.r = image.Sample(textureSampler, uv + colOffset).r; - - }else if (rnd < 0.66){ - outCol.g = image.Sample(textureSampler, uv + colOffset).g; - - } else{ - outCol.b = image.Sample(textureSampler, uv + colOffset).b; - } - - return outCol; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24422,680 +16081,247 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlowShader { +function Set-OBSSourceFilterName { -[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] +[Alias('obs.powershell.websocket.SetSourceFilterName')] param( -# Set the glow_percent of OBSGlowShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSGlowShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSGlowShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSGlowShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed of OBSGlowShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] -[Int32] -$PulseSpeed, -# Set the ease of OBSGlowShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the notes of OBSGlowShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newFilterName')] +[string] +$NewFilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glow' -$ShaderNoun = 'OBSGlowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int glow_percent< - string label = "Glow percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform string notes< - string widget_type = "info"; -> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; - - -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; -} - -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 offsets[4]; - offsets[0] = float2(-0.1, 0.125); - offsets[1] = float2(-0.1, -0.125); - offsets[2] = float2(0.1, -0.125); - offsets[3] = float2(0.1, 0.125); - - // convert input for vector math - float4 col = image.Sample(textureSampler, v_in.uv); - float blur_amount = float(blur) /100.0; - float glow_amount = float(glow_percent) * 0.01; - float speed = float(pulse_speed) * 0.01; - float luminance_floor = float(min_brightness) /100.0; - float luminance_ceiling = float(max_brightness) /100.0; - if (col.a > 0.0) - { - //circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - // simple glow calc - for (int n = 0; n < 4; n++) { - b = BlurStyler(t, 0, c, d, ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - col += (ncolor * (glow_amount * b)); - } - } - } - return col; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } - } -} - -} +} } #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGradientShader { +function Set-OBSSourceFilterSettings { -[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] +[Alias('obs.powershell.websocket.SetSourceFilterSettings')] param( -# Set the start_color of OBSGradientShader -[Alias('start_color')] -[ComponentModel.DefaultBindingProperty('start_color')] -[String] -$StartColor, -# Set the start_step of OBSGradientShader -[Alias('start_step')] -[ComponentModel.DefaultBindingProperty('start_step')] -[Single] -$StartStep, -# Set the middle_color of OBSGradientShader -[Alias('middle_color')] -[ComponentModel.DefaultBindingProperty('middle_color')] -[String] -$MiddleColor, -# Set the middle_step of OBSGradientShader -[Alias('middle_step')] -[ComponentModel.DefaultBindingProperty('middle_step')] -[Single] -$MiddleStep, -# Set the end_color of OBSGradientShader -[Alias('end_color')] -[ComponentModel.DefaultBindingProperty('end_color')] -[String] -$EndColor, -# Set the end_step of OBSGradientShader -[Alias('end_step')] -[ComponentModel.DefaultBindingProperty('end_step')] -[Single] -$EndStep, -# Set the alpha_percent of OBSGradientShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] -[Int32] -$AlphaPercent, -# Set the pulse_speed of OBSGradientShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] -[Int32] -$PulseSpeed, -# Set the ease of OBSGradientShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the rotate_colors of OBSGradientShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the horizontal of OBSGradientShader -[ComponentModel.DefaultBindingProperty('horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the vertical of OBSGradientShader -[ComponentModel.DefaultBindingProperty('vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the gradient_center_width_percentage of OBSGradientShader -[Alias('gradient_center_width_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] -[Int32] -$GradientCenterWidthPercentage, -# Set the gradient_center_height_percentage of OBSGradientShader -[Alias('gradient_center_height_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] -[Int32] -$GradientCenterHeightPercentage, -# Set the notes of OBSGradientShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gradient' -$ShaderNoun = 'OBSGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; -uniform float start_step< - string label = "Start step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.15; -uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; -uniform float middle_step< - string label = "Middle step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.4; -uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; -uniform float end_step< - string label = "End step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform int alpha_percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = true; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform bool horizontal; -uniform bool vertical; -uniform int gradient_center_width_percentage< - string label = "gradient center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int gradient_center_height_percentage< - string label = "gradient center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; - -float EaseInOutCircTimer(float t, float b, float c, float d) { - t /= d / 2; - if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; - t -= 2; - return c / 2 * (sqrt(1 - t * t) + 1) + b; -} - -float BlurStyler(float t, float b, float c, float d, bool ease) -{ - if (ease) return EaseInOutCircTimer(t, 0, c, d); - return t; -} - -struct gradient -{ - float4 color; - float step; -}; - - -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.14159265f;//acos(-1); - float speed = float(pulse_speed) * 0.01; - float alpha = float(alpha_percent) * 0.01; - - //circular easing variable - float t = sin(elapsed_time * speed) * 2 - 1; - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); - float4 color = image.Sample(textureSampler, v_in.uv); - float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; - float4 gray = float4(luminance,luminance,luminance, 1); - - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - b = BlurStyler(t, 0, c, d, ease); - - const int no_colors = 3; - float4 s_color = start_color; - float4 m_color = middle_color; - float4 e_color = end_color; - - if (rotate_colors) - { - // get general time number between 0 and 4 - float tx = (b + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls - - if (tx <= 2.0) - { - s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - } - - if ((tx >= 1.0) && (tx <= 3.0)) - { - s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - } - - if (tx >= 2.0) - { - s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - } - - if (tx < 0) - { - s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - } - } - - float4 colors[no_colors]; - colors[0] =s_color; - colors[1] = m_color; - colors[2] = e_color; - float step[no_colors]; - step[0] = start_step; - step[1] = middle_step; - step[2] = end_step; - - float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); - float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); - float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); - float dist = distance(v_in.uv, gradient_center); - if (horizontal && (vertical == false)) - { - dist = distance(v_in.uv.y, gradient_center.y); - } - if (vertical && (horizontal == false)) - { - dist = distance(v_in.uv.x, gradient_center.x); - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float4 col = colors[0]; - for (int i = 1; i < no_colors; ++i) { - col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); - } - col.a = clamp(alpha, 0.0, 1.0); - if (Apply_To_Alpha_Layer == false) - color.a = alpha; - if (Apply_To_Specific_Color) - { - col.a = alpha; - float4 original_color = image.Sample(textureSampler, v_in.uv); - col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - // result = float4(redness, greenness,blueness,1); - //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); - //color.rgb += col * alpha; - //color.a += clamp(1.0 - alpha, 0.0,1.0); - ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); - //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); - color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); - - } - return color; - - -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25104,191 +16330,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHalftoneShader { +function Set-OBSStreamServiceSettings { -[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] +[Alias('obs.powershell.websocket.SetStreamServiceSettings')] param( -# Set the threshold of OBSHalftoneShader -[ComponentModel.DefaultBindingProperty('threshold')] -[Single] -$Threshold, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceType')] +[string] +$StreamServiceType, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceSettings')] +[PSObject] +$StreamServiceSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'halftone' -$ShaderNoun = 'OBSHalftoneShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.1415926535897932384626433832795 -#define PI180 float(PI / 180.0) -uniform float threshold< - string label = "Threshold"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.6; -float sind(float a) -{ - return sin(a * PI180); -} - -float cosd(float a) -{ - return cos(a * PI180); -} - -float added(float2 sh, float sa, float ca, float2 c, float d) -{ - return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); -} - -float4 mainImage(VertData v_in) : TARGET -{ - // Halftone dot matrix shader - // @author Tomek Augustyn 2010 - - // Ported from my old PixelBender experiment - // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk - - float coordX = v_in.uv.x; - float coordY = v_in.uv.y; - float2 dstCoord = float2(coordX, coordY); - float2 rotationCenter = float2(0.5, 0.5); - float2 shift = dstCoord - rotationCenter; - - float dotSize = 3.0; - float angle = 45.0; - - float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); - float4 srcPixel = image.Sample(textureSampler, dstCoord); - - float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; - float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); - - // uncomment to see how the raster pattern looks - // gray = rasterPattern; - - return float4(gray, gray, gray, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25297,212 +16442,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHeatWaveSimpleShader { +function Set-OBSStudioModeEnabled { -[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] +[Alias('obs.powershell.websocket.SetStudioModeEnabled')] param( -# Set the Rate of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Rate')] -[Single] -$Rate, -# Set the Strength of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Single] -$Strength, -# Set the Distortion of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Distortion')] -[Single] -$Distortion, -# Set the Opacity of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Opacity')] -[Single] -$Opacity, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('studioModeEnabled')] +[switch] +$StudioModeEnabled, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'heat-wave-simple' -$ShaderNoun = 'OBSHeatWaveSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Heat Wave Simple, Version 0.03, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// -// Description: -// Generate a crude pseudo heat wave displacement on an image source. -// -// Based on: https://www.shadertoy.com/view/td3GRn by Dombass -// -// Changelog: -// 0.03 - Added Opacity control -// 0.02 - Added crude Rate, Strength, and Distortion controls -// 0.01 - Initial release - -uniform float Rate< - string label = "Rate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 5.0; -uniform float Strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = -25.0; - float maximum = 25.0; - float step = 0.01; -> = 1.0; -uniform float Distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 3.0; - float maximum = 20.0; - float step = 0.01; -> = 10.0; -uniform float Opacity< - string label = "Opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.00; - -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float distort = clamp(Distortion, 3.0, 20.0); - // Time varying pixel color - float jacked_time = Rate*elapsed_time; - float2 scale = float2(0.5, 0.5); - float str = clamp(Strength, -25.0, 25.0) * 0.01; - - uv += str * sin(scale*jacked_time + length( uv ) * distort); - float4 c = image.Sample( textureSampler, uv); - c.a *= saturate(Opacity*0.01); - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25511,321 +16549,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHexagonShader { +function Set-OBSTBarPosition { -[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] +[Alias('obs.powershell.websocket.SetTBarPosition')] param( -# Set the Hex_Color of OBSHexagonShader -[Alias('Hex_Color')] -[ComponentModel.DefaultBindingProperty('Hex_Color')] -[String] -$HexColor, -# Set the Alpha_Percent of OBSHexagonShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Int32] -$AlphaPercent, -# Set the Quantity of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Quantity')] -[Single] -$Quantity, -# Set the Border_Width of OBSHexagonShader -[Alias('Border_Width')] -[ComponentModel.DefaultBindingProperty('Border_Width')] -[Int32] -$BorderWidth, -# Set the Blend of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Blend')] -[Management.Automation.SwitchParameter] -$Blend, -# Set the Equilateral of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Equilateral')] -[Management.Automation.SwitchParameter] -$Equilateral, -# Set the Zoom_Animate of OBSHexagonShader -[Alias('Zoom_Animate')] -[ComponentModel.DefaultBindingProperty('Zoom_Animate')] -[Management.Automation.SwitchParameter] -$ZoomAnimate, -# Set the Speed_Percent of OBSHexagonShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Int32] -$SpeedPercent, -# Set the Glitch of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Distort_X of OBSHexagonShader -[Alias('Distort_X')] -[ComponentModel.DefaultBindingProperty('Distort_X')] -[Single] -$DistortX, -# Set the Distort_Y of OBSHexagonShader -[Alias('Distort_Y')] -[ComponentModel.DefaultBindingProperty('Distort_Y')] -[Single] -$DistortY, -# Set the Offset_X of OBSHexagonShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSHexagonShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the notes of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('position')] +[ValidateRange(0,1)] +[double] +$Position, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('release')] +[switch] +$Release, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hexagon' -$ShaderNoun = 'OBSHexagonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 Hex_Color; -uniform int Alpha_Percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform float Quantity< - string label = "Quantity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 25; -uniform int Border_Width< - string label = "Border Width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 115; - int step = 1; -> = 15; // <- -15 to 85, -15 off top -uniform bool Blend; -uniform bool Equilateral; -uniform bool Zoom_Animate; -uniform int Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool Glitch; -uniform float Distort_X< - string label = "Distort X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Distort_Y< - string label = "Distort Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Offset_Y< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform string notes< - string widget_type = "info"; ->= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; - -float mod(float x, float y) -{ - return x - y * floor(x/y); -} - -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x/y); -} - -// 0 on edges, 1 in non_edge -float hex(float2 p) { - float xyratio = 1; - if (Equilateral) - xyratio = uv_size.x /uv_size.y; - - // calc p - p.x = mul(p.x,xyratio); - p.y += mod(floor(p.x) , 2.0)*0.5; - p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); - return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); - float alpha = float(Alpha_Percent) * 0.01; - float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); - float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; - float speed = float(Speed_Percent) * 0.01; - float time = (1 + sin(elapsed_time * speed))*0.5; - if (Zoom_Animate) - quantity *= time; - - // create a (pos)ition reference, hex radius and smoothstep out the non_edge - float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); - if (Glitch) - quantity *= lerp(pos.x, pos.y, rand_f); - float2 p = (pos * quantity); // number of hexes to be created - float r = (1.0 -0.7)*0.5; // cell default radius - float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge - // make the border colorable - non_edge is scaled - float4 c = float4(non_edge, non_edge,non_edge,1.0) ; - if (non_edge < 1) - { - c = Hex_Color; - c.a = alpha; - if (Blend) - c = lerp(rgba, c, 1 - non_edge); - return lerp(rgba,c,alpha); - } - return lerp(rgba, c * rgba, alpha); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25834,316 +16662,243 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHslHsvSaturationShader { +function Set-OBSVideoSettings { -[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] +[Alias('obs.powershell.websocket.SetVideoSettings')] param( -# Set the hslSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] -[Single] -$HslSaturationFactor, -# Set the hslGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslGamma')] -[Single] -$HslGamma, -# Set the hsvSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] -[Single] -$HsvSaturationFactor, -# Set the hsvGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvGamma')] -[Single] -$HsvGamma, -# Set the adjustmentOrder of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('adjustmentOrder')] -[Int32] -$AdjustmentOrder, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('fpsNumerator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsNumerator, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +[ComponentModel.DefaultBindingProperty('fpsDenominator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsDenominator, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseWidth')] +[ValidateRange(1,4096)] +[double] +$BaseWidth, -process { -$shaderName = 'hsl_hsv_saturation' -$ShaderNoun = 'OBSHslHsvSaturationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseHeight')] +[ValidateRange(1,4096)] +[double] +$BaseHeight, -// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputWidth')] +[ValidateRange(1,4096)] +[double] +$OutputWidth, -uniform float hslSaturationFactor< - string label = "HSL Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputHeight')] +[ValidateRange(1,4096)] +[double] +$OutputHeight, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform float hslGamma< - string label = "HSL Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float hsvSaturationFactor< - string label = "HSV Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; +process { -uniform float hsvGamma< - string label = "HSV Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform int adjustmentOrder< - string label = "Order"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; - int option_1_value = 2; - string option_1_label = "HSL first, then HSV"; - int option_2_value = 3; - string option_2_label = "HSV first, then HSL"; -> = 1; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// HSV conversion + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float3 rgb2hsv(float3 c) { - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -float3 hsv2rgb(float3 c) { - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -// HSL conversion + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 rgb2hsl(float3 c) { - float maxVal = max(c.r, max(c.g, c.b)); - float minVal = min(c.r, min(c.g, c.b)); - float delta = maxVal - minVal; - float h = 0.0; - float s = 0.0; - float l = (maxVal + minVal) / 2.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if(delta != 0) { - if(l < 0.5) s = delta / (maxVal + minVal); - else s = delta / (2.0 - maxVal - minVal); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); - else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; - else h = (c.r - c.g) / delta + 4.0; +} - h /= 6.0; - } - return float3(h, s, l); -} +} -float hue2rgb(float p, float q, float t) { - if(t < 0.0) t += 1.0; - if(t > 1.0) t -= 1.0; - if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; - if(t < 1.0/2.0) return q; - if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; - return p; -} + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSOutput { -float3 hsl2rgb(float3 c) { - float r, g, b; - if(c.y == 0.0) { - r = g = b = c.z; - } else { - float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; - float p = 2.0 * c.z - q; - r = hue2rgb(p, q, c.x + 1.0/3.0); - g = hue2rgb(p, q, c.x); - b = hue2rgb(p, q, c.x - 1.0/3.0); - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] +[Alias('obs.powershell.websocket.StartOutput')] +param( - return float3(r, g, b); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float3 adjustColorWithOrder(float3 originalColor) { - if (adjustmentOrder == 1) { - // Parallel adjustment (both HSL and HSV operate on the original image and then blend) - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 hslAdjustedColor = hsl2rgb(hslAdjusted); - - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); - - float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; - return finalColor; - } - else if (adjustmentOrder == 2) { - // HSL first, then HSV - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 afterHSL = hsl2rgb(hslAdjusted); - float3 hsvAdjusted = rgb2hsv(afterHSL); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - return hsv2rgb(hsvAdjusted); - } - else if (adjustmentOrder == 3) { - // HSV first, then HSL - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 afterHSV = hsv2rgb(hsvAdjusted); - float3 hslAdjusted = rgb2hsl(afterHSV); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - return hsl2rgb(hslAdjusted); - } - return originalColor; // Default to original color in case of unexpected values -} -// Final composite +process { -float4 mainImage(VertData v_in) : TARGET -{ - float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; - float3 adjustedColor = adjustColorWithOrder(originalColor); - return float4(adjustedColor, 1.0); // preserving the original alpha -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26152,235 +16907,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHueRotatonShader { +function Start-OBSRecord { -[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] +[Alias('obs.powershell.websocket.StartRecord')] param( -# Set the Speed of OBSHueRotatonShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Hue_Override of OBSHueRotatonShader -[Alias('Hue_Override')] -[ComponentModel.DefaultBindingProperty('Hue_Override')] -[Management.Automation.SwitchParameter] -$HueOverride, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hue-rotaton' -$ShaderNoun = 'OBSHueRotatonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hue Rotation shader, version 1.0 for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Description: -// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue -// override option is provided to force a specific rotating hue instead of the original image''s hue. -// -// Changelog: -// 1.0 - Initial release -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as published by the Free Software Foundation. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 10.00; -uniform bool Hue_Override = false; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return saturate(float3(R,G,B)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -#define Epsilon 1e-10 - -float3 RGBtoHCV(in float3 RGB) -{ - // Based on work by Sam Hocevar and Emil Persson - float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); - float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); - float C = Q.x - min(Q.w, Q.y); - float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); - return float3(H, C, Q.x); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float3 HSVtoRGB(in float3 HSV) -{ - float3 RGB = HUEtoRGB(HSV.x); - return ((RGB - 1) * HSV.y + 1) * HSV.z; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 RGBtoHSV(in float3 RGB) -{ - float3 HCV = RGBtoHCV(RGB); - float S = HCV.y / (HCV.z + Epsilon); - return float3(HCV.x, S, HCV.z); } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 col_in = image.Sample(textureSampler, uv); - float3 col_out; - float3 HSV = RGBtoHSV(col_in.rgb); - if(Hue_Override) - HSV.x = elapsed_time * Speed * 0.01; - else - HSV.x += elapsed_time * Speed * 0.01; +} - // Normalize Hue - HSV.x = frac(HSV.x); - - col_out = HSVtoRGB(HSV); - return float4(col_out, col_in.a); -} + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSReplayBuffer { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] +[Alias('obs.powershell.websocket.StartReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26389,192 +17111,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSIntensityScopeShader { +function Start-OBSStream { -[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] +[Alias('obs.powershell.websocket.StartStream')] param( -# Set the gain of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('gain')] -[Single] -$Gain, -# Set the blend of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('blend')] -[Single] -$Blend, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'intensity-scope' -$ShaderNoun = 'OBSIntensityScopeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Robin Green, Dec 2016 -// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. -// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro -uniform float gain< - string label = "Gain"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 1.00; - float step = 0.01; -> = 0.3; -uniform float blend< - string label = "Blend"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.y = 1.0 - uv.y; - - // calculate the intensity bucket for this pixel based on column height (padded at the top) - const float max_value = 270.0; - const float buckets = 512.0; - float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); - float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); - - // count the count the r,g,b and luma in this column that match the bucket - float4 count = float4(0.0, 0.0, 0.0, 0.0); - for( int i=0; i < 512; ++i ) { - float j = float(i) / buckets; - float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; - - // calculate the Rec.709 luma for this pixel - pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; - float4 logpixel = log(pixel); - if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; - if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; - if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; - if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; - } - - // sum luma into RGB, tweak log intensity for readability - count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; - - // output - return count; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26583,266 +17213,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInvertLumaShader { +function Start-OBSVirtualCam { -[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] +[Alias('obs.powershell.websocket.StartVirtualCam')] param( -# Set the Invert_Color of OBSInvertLumaShader -[Alias('Invert_Color')] -[ComponentModel.DefaultBindingProperty('Invert_Color')] -[Management.Automation.SwitchParameter] -$InvertColor, -# Set the Invert_Luma of OBSInvertLumaShader -[Alias('Invert_Luma')] -[ComponentModel.DefaultBindingProperty('Invert_Luma')] -[Management.Automation.SwitchParameter] -$InvertLuma, -# Set the Gamma_Correction of OBSInvertLumaShader -[Alias('Gamma_Correction')] -[ComponentModel.DefaultBindingProperty('Gamma_Correction')] -[Management.Automation.SwitchParameter] -$GammaCorrection, -# Set the Test_Ramp of OBSInvertLumaShader -[Alias('Test_Ramp')] -[ComponentModel.DefaultBindingProperty('Test_Ramp')] -[Management.Automation.SwitchParameter] -$TestRamp, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'invert-luma' -$ShaderNoun = 'OBSInvertLumaShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Invert shader 1.0 - for OBS Shaderfilter -// Copyright 2021 by SkeletonBow -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv - -// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling - -uniform bool Invert_Color = false; -uniform bool Invert_Luma = true; -uniform bool Gamma_Correction = true; -uniform bool Test_Ramp = false; -float3 encodeSRGB(float3 linearRGB) -{ - float3 a = float3(12.92,12.92,12.92) * linearRGB; - float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); - float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); - return float3(lerp(a, b, c)); -} -float3 decodeSRGB(float3 screenRGB) -{ - float3 a = screenRGB / float3(12.92,12.92,12.92); - float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); - float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); - return float3(lerp(a, b, c)); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float3 RGBtoYUV(float3 color) -{ - // YUV matriz (BT709 luma coefficients) -#ifdef OPENGL - float3x3 toYUV = float3x3( - float3(0.2126, -0.09991, 0.615), - float3(0.7152, -0.33609, -0.55861), - float3(0.0722, 0.436, -0.05639)); -#else - float3x3 toYUV = { - { 0.2126, -0.09991, 0.615 }, - { 0.7152, -0.33609, -0.55861 }, - { 0.0722, 0.436, -0.05639 }, - }; -#endif - return mul(color, toYUV); -} - -float3 YUVtoRGB(float3 color) -{ - // YUV matriz (BT709) -#ifdef OPENGL - float3x3 fromYUV = float3x3( - float3(1.000, 1.000, 1.000), - float3(0.0, -0.21482, 2.12798), - float3(1.28033, -0.38059, 0.0)); -#else - float3x3 fromYUV = { - { 1.000, 1.000, 1.000 }, - { 0.0, -0.21482, 2.12798 }, - { 1.28033, -0.38059, 0.0 }, - }; -#endif - return mul(color, fromYUV); -} - -float3 generate_ramps(float3 color, float2 uv) -{ - float3 ramp = float3(0.0, 0.0, 0.0); - if(uv.y < 0.2) - ramp.r = uv.x; // Red ramp - else if(uv.y < 0.4) - ramp.g = uv.x; // Green ramp - else if(uv.y < 0.6) - ramp.b = uv.x; // Blue ramp - else if(uv.y < 0.8) - ramp = float3(uv.x, uv.x, uv.x); // Grey ramp - else - ramp = HUEtoRGB(uv.x); // Hue rainbow - - return ramp; -} - -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 obstex = image.Sample( textureSampler, uv ); - float3 color = obstex.rgb; - // Apply sRGB gamma transfer encode - if(Gamma_Correction) color = encodeSRGB( color ); - // Override display with test patterns to visually see what is happening - if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); - // RGB color invert - if( Invert_Color ) { - color = float3(1.0, 1.0, 1.0) - color; - } - // YUV luma invert - if( Invert_Luma ) { - float3 yuv = RGBtoYUV( color ); - yuv.x = 1.0 - yuv.x; - color = YUVtoRGB(yuv); - } - // Apply sRGB gamma transfer decode - if(Gamma_Correction) color = decodeSRGB( color ); - return float4(color, obstex.a); -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26851,240 +17315,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminance2Shader { +function Stop-OBSOutput { -[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] +[Alias('obs.powershell.websocket.StopOutput')] param( -# Set the color of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the lumaMax of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMax')] -[Single] -$LumaMax, -# Set the lumaMin of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMaxSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] -[Single] -$LumaMaxSmooth, -# Set the lumaMinSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the invertImageColor of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'luminance2' -$ShaderNoun = 'OBSLuminance2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 color; -uniform float lumaMax< - string label = "Luma Max"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.05; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMaxSmooth< - string label = "Luma Max Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.10; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; -uniform string notes< - string widget_type = "info"; -> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; - -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} - -float4 mainImage(VertData v_in) : TARGET -{ - - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - - //intensity = min(max(intensity,minIntensity),maxIntensity); - float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); - float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); - - float amask = clo * chi; - if (invertAlphaChannel) - { - amask = 1.0 - amask; - } - rgba *= color; - rgba.a = clamp(amask, 0.0, 1.0); - - } - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -27093,298 +17422,203 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceAlphaShader { +function Stop-OBSRecord { -[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] +[Alias('obs.powershell.websocket.StopRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSLuminanceAlphaShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSLuminanceAlphaShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSLuminanceAlphaShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSLuminanceAlphaShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSLuminanceAlphaShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSLuminanceAlphaShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the color_matrix of OBSLuminanceAlphaShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the mul_val of OBSLuminanceAlphaShader -[Alias('mul_val')] -[ComponentModel.DefaultBindingProperty('mul_val')] -[Single] -$MulVal, -# Set the add_val of OBSLuminanceAlphaShader -[Alias('add_val')] -[ComponentModel.DefaultBindingProperty('add_val')] -[Single] -$AddVal, -# Set the level of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('level')] -[Single] -$Level, -# Set the invertAlphaChannel of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'luminance_alpha' -$ShaderNoun = 'OBSLuminanceAlphaShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform float4x4 color_matrix; -uniform float4 color; -uniform float mul_val< - string label = "Mulitply"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float add_val< - string label = "Add"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 0.0; -uniform float level< - string label = "Level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> =1.0; -uniform bool invertAlphaChannel; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -VertDataOut mainTransform(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); - vert_out.uv = v_in.uv * mul_val + add_val; - vert_out.uv2 = v_in.uv ; - return vert_out; } -/*float3 GetLuminance(float4 rgba) -{ - float red = rbga.r; - float green = rgba.g; - float blue = rgba.b; - return (.299 * red) + (.587 * green) + (.114 * blue); -}*/ -float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv) ; - if (rgba.a > 0.0) - { - - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; - } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - - } - return rgba; -} +} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = PSAlphaMaskRGBA(v_in); - } -} + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSReplayBuffer { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] +[Alias('obs.powershell.websocket.StopReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -27393,205 +17627,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceShader { +function Stop-OBSStream { -[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] +[Alias('obs.powershell.websocket.StopStream')] param( -# Set the color of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the level of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('level')] -[Single] -$Level, -# Set the invertImageColor of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Luminance' -$ShaderNoun = 'OBSLuminanceShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 22, 2022 -uniform float4 color; -uniform float level< - string label = "Level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; -uniform string notes< - string widget_type = "info"; -> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - - //intensity = min(max(intensity,minIntensity),maxIntensity); - - - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; - } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - - } - return rgba; -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSVirtualCam { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] +[Alias('obs.powershell.websocket.StopVirtualCam')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -27600,439 +17831,219 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMatrixShader { +function Switch-OBSInputMute { -[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] +[Alias('obs.powershell.websocket.ToggleInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSMatrixShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSMatrixShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSMatrixShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSMatrixShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSMatrixShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSMatrixShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSMatrixShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSMatrixShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSMatrixShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the mouse of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('mouse')] -[Single[]] -$Mouse, -# Set the Invert_Direction of OBSMatrixShader -[Alias('Invert_Direction')] -[ComponentModel.DefaultBindingProperty('Invert_Direction')] -[Management.Automation.SwitchParameter] -$InvertDirection, -# Set the lumaMin of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Ratio of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('Ratio')] -[Single] -$Ratio, -# Set the Alpha_Percentage of OBSMatrixShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSMatrixShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'matrix' -$ShaderNoun = 'OBSMatrixShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float -uniform float4x4 ViewProj; -uniform texture2d image; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float2 mouse< - string label = "Virtual Mouse Coordinates"; - string widget_type = "slider"; - float2 minimum = {0, 0}; - float2 maximum = {100., 100.}; - float2 scale = {.01, .01}; - float2 step = {.01, .01}; -> = {0., 0.}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -int2 iMouse() { - return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); } -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; -/* ps start +} -*/ + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSOutput { -uniform bool Invert_Direction< - string label = "Invert Direction"; -> = true; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float Ratio< - string label = "Ratio"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 4.0; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100; // -uniform bool Apply_To_Alpha_Layer = true; - -#define PI2 6.28318530718 -#define PI 3.1416 - - -float vorocloud(float2 p){ - float f = 0.0; - float flow = 1.0; - float time = elapsed_time; - if(Invert_Direction){ - flow *= -1; - } - /* - //periodically stop - if (loops % 16 >= 8.0) - { - time = local_time - elapsed_time; - } - */ - - float r = clamp(Ratio,-50,50); - float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); - p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); - - float2 pts[4]; - - pts[0] = float2(0.5, 0.6); - pts[1] = float2(-0.4, 0.4); - pts[2] = float2(0.2, -0.7); - pts[3] = float2(-0.3, -0.4); - - float d = 5.0; - - for(int i = 0; i < 4; i++){ - pts[i].x += 0.03 * cos(float(i)) + p.x; - pts[i].y += 0.03 * sin(float(i)) + p.y; - d = min(d, distance(pts[i], pp)); - } - - f = 2.0 * pow(1.0 - 0.3 * d, 13.0); - - f = min(f, 1.0); - - return f; -} - -vec4 scene(float2 UV){ - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - - float x = UV.x; - float y = UV.y; - - float2 p = float2(x, y) - 0.5; - - vec4 col = vec4(0.0,0.0,0.0,0.0); - col.g += 0.02; - - float v = vorocloud(p); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.6 * v; - col.b += 0.5 * pow(v, 5.0); - - - v = vorocloud(p * 2.0); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.2 * v; - col.b += 0.01 * pow(v, 5.0); - - col.a = 1.0; - float luma = dot(col.rgb,float3(0.299,0.587,0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min,0.0,1.0); - - float4 original_color = image.Sample(textureSampler, UV); - - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - if (Apply_To_Alpha_Layer == false) - original_color.a = alpha; - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } - else - { - col.a = original_color.a; - } - - return col; -} - -void mainImage( out vec4 fragColor, in vec2 fragCoord ) -{ - vec2 uv = fragCoord.xy / uv_size; - fragColor = scene(uv); -} - -/*ps end*/ +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] +[Alias('obs.powershell.websocket.ToggleOutput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} +process { -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -28041,147 +18052,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMultiplyShader { +function Switch-OBSRecord { -[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] +[Alias('obs.powershell.websocket.ToggleRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the other_image of OBSMultiplyShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] -[String] -$OtherImage, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'multiply' -$ShaderNoun = 'OBSMultiplyShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return base * other; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -28190,99 +18155,481 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSNightSkyShader { +function Switch-OBSRecordPause { -[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] +[Alias('obs.powershell.websocket.ToggleRecordPause')] param( -# Set the speed of OBSNightSkyShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the Include_Clouds of OBSNightSkyShader -[Alias('Include_Clouds')] -[ComponentModel.DefaultBindingProperty('Include_Clouds')] -[Management.Automation.SwitchParameter] -$IncludeClouds, -# Set the Include_Moon of OBSNightSkyShader -[Alias('Include_Moon')] -[ComponentModel.DefaultBindingProperty('Include_Moon')] -[Management.Automation.SwitchParameter] -$IncludeMoon, -# Set the center_width_percentage of OBSNightSkyShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSNightSkyShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, -# Set the Alpha_Percentage of OBSNightSkyShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSNightSkyShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSNightSkyShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the number_stars of OBSNightSkyShader -[Alias('number_stars')] -[ComponentModel.DefaultBindingProperty('number_stars')] -[Int32] -$NumberStars, -# Set the SKY_COLOR of OBSNightSkyShader -[Alias('SKY_COLOR')] -[ComponentModel.DefaultBindingProperty('SKY_COLOR')] -[String] -$SKYCOLOR, -# Set the STAR_COLOR of OBSNightSkyShader -[Alias('STAR_COLOR')] -[ComponentModel.DefaultBindingProperty('STAR_COLOR')] -[String] -$STARCOLOR, -# Set the LIGHT_SKY of OBSNightSkyShader -[Alias('LIGHT_SKY')] -[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] -[String] -$LIGHTSKY, -# Set the SKY_LIGHTNESS of OBSNightSkyShader -[Alias('SKY_LIGHTNESS')] -[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] -[Single] -$SKYLIGHTNESS, -# Set the MOON_COLOR of OBSNightSkyShader -[Alias('MOON_COLOR')] -[ComponentModel.DefaultBindingProperty('MOON_COLOR')] +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSReplayBuffer { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] +[Alias('obs.powershell.websocket.ToggleReplayBuffer')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSStream { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] +[Alias('obs.powershell.websocket.ToggleStream')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSVirtualCam { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] +[Alias('obs.powershell.websocket.ToggleVirtualCam')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBS3dPanelShader { + +[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] +param( +# Set the credits of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('credits')] [String] -$MOONCOLOR, -# Set the moon_size of OBSNightSkyShader -[Alias('moon_size')] -[ComponentModel.DefaultBindingProperty('moon_size')] -[Single] -$MoonSize, -# Set the moon_bump_size of OBSNightSkyShader -[Alias('moon_bump_size')] -[ComponentModel.DefaultBindingProperty('moon_bump_size')] +$Credits, +# Set the scale of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('scale')] [Single] -$MoonBumpSize, -# Set the Moon_Position_x of OBSNightSkyShader -[Alias('Moon_Position_x')] -[ComponentModel.DefaultBindingProperty('Moon_Position_x')] +$Scale, +# Set the tilt_x_deg of OBS3dPanelShader +[Alias('tilt_x_deg')] +[ComponentModel.DefaultBindingProperty('tilt_x_deg')] +[Single] +$TiltXDeg, +# Set the tilt_y_deg of OBS3dPanelShader +[Alias('tilt_y_deg')] +[ComponentModel.DefaultBindingProperty('tilt_y_deg')] +[Single] +$TiltYDeg, +# Set the tilt_z_deg of OBS3dPanelShader +[Alias('tilt_z_deg')] +[ComponentModel.DefaultBindingProperty('tilt_z_deg')] +[Single] +$TiltZDeg, +# Set the pos_x of OBS3dPanelShader +[Alias('pos_x')] +[ComponentModel.DefaultBindingProperty('pos_x')] +[Single] +$PosX, +# Set the pos_y of OBS3dPanelShader +[Alias('pos_y')] +[ComponentModel.DefaultBindingProperty('pos_y')] +[Single] +$PosY, +# Set the thickness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('thickness')] +[Single] +$Thickness, +# Set the radius_fb of OBS3dPanelShader +[Alias('radius_fb')] +[ComponentModel.DefaultBindingProperty('radius_fb')] +[Single] +$RadiusFb, +# Set the brightness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('brightness')] [Single] -$MoonPositionX, -# Set the Moon_Position_y of OBSNightSkyShader -[Alias('Moon_Position_y')] -[ComponentModel.DefaultBindingProperty('Moon_Position_y')] +$Brightness, +# Set the light_position of OBS3dPanelShader +[Alias('light_position')] +[ComponentModel.DefaultBindingProperty('light_position')] +[Int32] +$LightPosition, +# Set the wiggle of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('wiggle')] [Single] -$MoonPositionY, +$Wiggle, +# Set the wiggle_rot of OBS3dPanelShader +[Alias('wiggle_rot')] +[ComponentModel.DefaultBindingProperty('wiggle_rot')] +[Management.Automation.SwitchParameter] +$WiggleRot, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28313,297 +18660,265 @@ $UseShaderTime process { -$shaderName = 'night_sky' -$ShaderNoun = 'OBSNightSkyShader' +$shaderName = '3d-panel' +$ShaderNoun = 'OBS3dPanelShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 -// https://github.com/Oncorporation/obs-shaderfilter -//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float speed< - string label = "Speed"; +//based on https://x.com/HoraiChan/status/1986268258883010766 + +uniform string credits< + string widget_type = "info"; +> = "Based on effect by Horaiken"; + +uniform float scale< + string label = "大きさ / Scale"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; + float minimum = 0.25; + float maximum = 3.00; + float step = 0.001; +> = 1.0; +uniform float tilt_x_deg< + string label = "縦方向の傾き(X) / Tilt (X)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; > = 20.0; -uniform bool Include_Clouds = true; -uniform bool Include_Moon = true; -uniform int center_width_percentage< - string label = "Center width percentage"; +uniform float tilt_y_deg< + string label = "横方向の傾き(Y) / Tilt (Y)"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "Center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 35.0; +uniform float tilt_z_deg< + string label = "回転(Z) / Roll (Z)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 95.0; // -uniform bool Apply_To_Image = false; -uniform bool Replace_Image_Color = false; -uniform int number_stars< - string label = "Number stars"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 0.0; +uniform float pos_x< + string label = "横位置 / Horizontal Position"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; // - -uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; -uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; -uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; -uniform float SKY_LIGHTNESS< - string label = "SKY LIGHTNESS"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float pos_y< + string label = "縦位置 / Vertical Position"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = .3; - - // Moon -uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; -uniform float moon_size< - string label = "Moon size"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float thickness< + string label = "厚み / Thickness"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; + float minimum = 0.00; + float maximum = 0.1; float step = 0.001; -> = 0.18; -uniform float moon_bump_size< - string label = "Moon bump size"; +> = 0.03; +uniform float radius_fb< + string label = "角丸 / Corner Radius"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.14; -uniform float Moon_Position_x< - string label = "Moon Position x"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.2; +uniform float brightness< + string label = "明るさ / Brightness"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.6; -uniform float Moon_Position_y< - string label = "Moon Position y"; + float minimum = 0.00; + float maximum = 2.00; + float step = 0.01; +> = 1.2; +uniform int light_position < + string label = "照明の位置 / Light Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "左側に光 / Light From Left"; + int option_1_value = 1; + string option_1_label = "右側に光 / Light From Right"; +> = 0; +uniform float wiggle < + string label = "ゆらゆら / Wiggle"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.3; - -#define PI 3.1416 - -//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU -float noise11(float p) { - return frac(sin(p*633.1847) * 9827.95); -} - -float noise21(float2 p) { - return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); -} + float minimum = 0.00; + float maximum = 2.50; + float step = 0.01; +> = 0.0; +uniform bool wiggle_rot < + string label = "角度もゆらゆら / Wiggle Rotation"; +>; -float2 noise22(float2 p) { - return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); -} +float hash1(float n){ return frac(sin(n)*43758.5453123); } -//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise -float cosineInterpolation(float a, float b, float x) { - float ft = x * PI; - float f = (1. - cos(ft)) * .5; - return a * (1. - f) + b * f; +float noise1D(float x) { + float i = floor(x); + float f = frac(x); + float u = f*f*(3.0 - 2.0*f); + return lerp(hash1(i), hash1(i+1.0), u); // 0..1 } -float smoothNoise11(float p, float dist) { - float prev = noise11(p-dist); - float next = noise11(p+dist); - - return cosineInterpolation(prev, next, .5); +float fbm1D(float x) { + float v = 0.0; + float a = 0.5; + float f = 1.0; + for(int k=0;k<4;k++){ + v += a * noise1D(x * f); + f *= 2.0; + a *= 0.5; + } + return v; } -float smoothNoise21(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - //smoothstep function: maybe change it later! - lv = lv*lv*(3.-2.*lv); - - float bl = noise21(id); - float br = noise21(id+float2(1.,0.)); - float b = lerp(bl, br, lv.x); - - float tl = noise21(id+float2(0.,1.)); - float tr = noise21(id+float2(1.,1.)); - float t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} +float saturate(float x) { return clamp(x, 0.0, 1.0); } -float2 smoothNoise22(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - lv = lv*lv*(3.-2.*lv); - - float2 bl = noise22(id); - float2 br = noise22(id+float2(1.,0.)); - float2 b = lerp(bl, br, lv.x); - - float2 tl = noise22(id+float2(0.,1.)); - float2 tr = noise22(id+float2(1.,1.)); - float2 t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} +float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } +float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } +float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } -float valueNoise11(float p) { - float c = smoothNoise11(p, 0.5); - c += smoothNoise11(p, 0.25)*.5; - c += smoothNoise11(p, 0.125)*.25; - c += smoothNoise11(p, 0.0625)*.125; - - return c /= .875; +// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) +float sdRoundRect2D(float2 p, float2 bxy, float r) { + float2 q = abs(p) - bxy + r; + return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; } -float valueNoise21(float2 uv) { - float c = smoothNoise21(uv, 4.); - c += smoothNoise21(uv, 8.)*.5; - c += smoothNoise21(uv, 16.)*.25; - c += smoothNoise21(uv, 32.)*.125; - c += smoothNoise21(uv, 64.)*.0625; - - return c /= .9375; +// 正面シルエット角丸 + Z方向に押し出し +float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { + float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); + float a = sdRoundRect2D(p.xy, b.xy, r_fb); + float dz = abs(p.z) - b.z; + return max(a, dz); } -float2 valueNoise22(float2 uv) { - float2 c = smoothNoise22(uv, 4.); - c += smoothNoise22(uv, 8.)*.5; - c += smoothNoise22(uv, 16.)*.25; - c += smoothNoise22(uv, 32.)*.125; - c += smoothNoise22(uv, 64.)*.0625; - - return c /= .9375; +// 法線 +float3 calcNormal(float3 p, float3 b, float rfb) { + const float e = 0.001; + float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); + float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); + float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); + float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); + return normalize(float3(dx,dy,dz)); } -float3 points(float2 p, float2 uv, float3 color, float size, float blur) { - float dist = distance(p, uv); - return color*smoothstep(size, size*(0.999-blur), dist); +// 照明 +float3 shade(float3 n, float3 v) { + float3 l; + if (light_position == 0) { // 左から光 + l = normalize(float3(-1.0, -0.1, 1.0)); + } + else { // 右から光 + l = normalize(float3( 1.0, -0.1, 1.0)); + } + float diff = saturate(dot(n,l)); + float rim = pow(1.0 - saturate(dot(n,v)), 2.0); + float li = 0.25 + 0.75*diff + 0.08*rim; + return float3(li, li, li); } -float mapInterval(float x, float a, float b, float c, float d) { - return (x-a)/(b-a) * (d-c) + c; -} +float4 mainImage(VertData v_in) : TARGET { + float2 uv = v_in.uv; -float blink(float time, float timeInterval) { - float halfInterval = timeInterval / 2.0; - //Get relative position in the bucket - float p = fmod(time, timeInterval); - - - if (p <= timeInterval / 2.) { - return smoothstep(0., 1., p/halfInterval); + // 画面座標(短辺基準) + float aspect = uv_size.x / uv_size.y; + float2 ndc = uv * 2.0 - 1.0; + ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); + float2 p2 = ndc; + p2.x *= aspect; + + // カメラ設定 + float3 ro = float3(0.0, 0.0, 3.2); + float3 rd = normalize(float3(p2, -4.0)); + + // 回転(Z→Y→X の順に逆回転) + float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); + ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); + rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); + + // 画面フィット(短辺基準)+ 厚み + float2 baseXY; + if (aspect > 1.0) { + baseXY = float2(1.0, 1.0 / aspect); } else { - return smoothstep(1., 0., (p-halfInterval)/halfInterval); + const float portraitMargin = 0.6; + baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); } -} + float3 b = float3(baseXY, thickness) * max(scale, 0.0001); -float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { - float dist = distance(p, uv); - float3 BumpSamples = float3(0.,0.,0.); + // Wiggle + float diag = length(2.0 * b); + float amp = 0.05 * wiggle * diag; + const float WSPD = 0.1; - if (dist < radius) { - float bumps = (1.-valueNoise21(uv*spin))*.1; - BumpSamples = float3(bumps, bumps, bumps); - } - return BumpSamples; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba;// = image.Sample(textureSampler, v_in.uv); - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st; + // 各軸に独立ノイズ + float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; + float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; + float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; + float3 woff = float3(wx, wy, wz); - // Normalized pixel coordinates (from 0 to 1) - float2 uv = v_in.uv; - float2 ouv = uv; - uv -= .5; - uv.x *= uv_size.x/uv_size.y; - - float2 seed = toCenter / uv_size.xy; + float rotAmp = radians(12.0) * wiggle; - float time = elapsed_time + seed.x*speed; - - //float3 col = float3(0.0); - //float m = valueNoise21(uv); - float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); + float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; + float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; - col *= SKY_LIGHTNESS - (1.-ouv.y); + float3 ro2 = ro; + float3 rd2 = rd; - //Add clouds - if (Include_Clouds) - { - float2 timeUv = uv; - timeUv.x += time*.1; - timeUv.y += valueNoise11(timeUv.x+.352)*.01; - float cloud = valueNoise21(timeUv); - col += cloud*.1; + if (wiggle_rot) { + ro2 = rotateX(ro2, wobX); + ro2 = rotateY(ro2, wobY); + rd2 = rotateX(rd2, wobX); + rd2 = rotateY(rd2, wobY); } - //Add stars in the top part of the scene - float timeInterval = speed *.5; //5.0 - float timeBucket = floor(time / timeInterval); - - float2 moonPosition = float2(-1000, -1000); - if (Include_Moon) - { - moonPosition = float2(Moon_Position_x, Moon_Position_y); - col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); - // Moon bumps - col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); + float t = 0.0; + float d = 0.0; + bool hit = false; + for (int i=0; i<64; i++) { + float3 pos = ro2 + rd2 * t; + d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); + if (d < 0.001) { hit = true; break; } + t += d; + if (t > 8.0) break; } - for (float i = 0.; i < clamp(number_stars,0,100); i++) { - float2 starPosition = float2(i/10., i/10.); - - starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); - starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); - - float starIntensity = blink(time+ (rand_f * i), timeInterval ); - //Hide stars that are behind the moon - if (distance(starPosition, moonPosition) > moon_size) { - col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; - col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; - } - } - //col = float3(blink(time, timeInterval)); - rgba = float4(col,alpha); + // ヒットしなければ完全透明(元ソースは非表示) + if (!hit) return float4(0.0, 0.0, 0.0, 0.0); - // Output to screen - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,alpha); - - } - return rgba; + float3 pos = ro2 + rd2 * t; + float3 pObj = pos - woff; + float3 n = calcNormal(pObj, b, radius_fb); + float3 vdir = normalize(-rd2); + + // テクスチャ貼り付け + float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); + float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; + + // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) + float4 texFront = image.Sample(textureSampler, uvTex); + float4 texEdge = image.Sample(textureSampler, uvTex); + float4 tex = lerp(texEdge, texFront, frontMask); + + // フロント面エッジ・ハイライト(細い線) + float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); + float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF + float edgeWidth = 0.02 * min(b.x, b.y); + float edgeIntensity = 0.6; + float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); + float edgeMask = frontMask * edgeProx; + tex.rgb *= (1.0 + edgeMask * edgeIntensity); + + // 照明 + float3 lightTerm = shade(n, vdir); + tex.rgb *= lightTerm; + + // 明るさスライダ + tex.rgb *= brightness; + + // 出力 + return float4(tex.rgb, 1.0); } ' @@ -28703,14 +19018,47 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOpacityShader { +function Get-OBS3dSwapTransitionShader { -[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] +[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] param( -# Set the Opacity of OBSOpacityShader -[ComponentModel.DefaultBindingProperty('Opacity')] +# Set the image_a of OBS3dSwapTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBS3dSwapTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBS3dSwapTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] [Single] -$Opacity, +$TransitionTime, +# Set the convert_linear of OBS3dSwapTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the reflection of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('reflection')] +[Single] +$Reflection, +# Set the perspective of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the depth of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('depth')] +[Single] +$Depth, +# Set the background_color of OBS3dSwapTransitionShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] +[String] +$BackgroundColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28741,27 +19089,108 @@ $UseShaderTime process { -$shaderName = 'opacity' -$ShaderNoun = 'OBSOpacityShader' +$shaderName = '3d_swap_transition' +$ShaderNoun = 'OBS3dSwapTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Opacity shader - for OBS Shaderfilter -// Copyright 2021 by SkeltonBowTV -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv +//based on https://www.shadertoy.com/view/MlXGzf -uniform float Opacity< - string label = "Opacity"; +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; + +uniform float reflection< + string label = "Reflection (0.4)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; + float minimum = 0.00; + float maximum = 1.00; float step = 0.01; -> = 100.00; // 0.00 - 100.00 percent +> = 0.4; +uniform float perspective< + string label = "Perspective (0.2)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = .2; +uniform float depth< + string label = "Depth (3.0)"; + string widget_type = "slider"; + float minimum = 1.00; + float maximum = 10.00; + float step = 0.1; +> = 3.; -float4 mainImage( VertData v_in ) : TARGET -{ - float4 color = image.Sample( textureSampler, v_in.uv ); - return float4( color.rgb, color.a * Opacity * 0.01 ); +#ifndef OPENGL +#define lessThan(a,b) (a < b) +#endif + + +uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; + +bool inBounds (float2 p) { + return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); +} + +float2 project (float2 p) { + return p * float2(1.0, -1.2) + float2(0.0, 2.22); +} + +float4 bgColor (float2 p, float2 pfr, float2 pto) { + float4 c = background_color; + pfr = project(pfr); + if (inBounds(pfr)) { + c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); + } + pto = project(pto); + if (inBounds(pto)) { + c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); + } + return c; +} + +float4 mainImage(VertData v_in) : TARGET { + float2 p = v_in.uv; + float2 pfr = float2(-1.,-1.); + float2 pto = float2(-1.,-1.); + + float progress = transition_time; + float size = lerp(1.0, depth, progress); + float persp = perspective * progress; + pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); + + size = lerp(1.0, depth, 1.-progress); + persp = perspective * (1.-progress); + pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); + + bool fromOver = progress < 0.5; + float4 rgba = background_color; + if (fromOver) { + if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + else { + if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; } ' } @@ -28860,18 +19289,15 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelShader { +function Get-OBSAddShader { -[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] +[Alias('Set-OBSAddShader','Add-OBSAddShader')] param( -# Set the Speed of OBSPagePeelShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Position of OBSPagePeelShader -[ComponentModel.DefaultBindingProperty('Position')] -[Single] -$Position, +# Set the other_image of OBSAddShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] +[String] +$OtherImage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28902,85 +19328,18 @@ $UseShaderTime process { -$shaderName = 'page-peel' -$ShaderNoun = 'OBSPagePeelShader' +$shaderName = 'Add' +$ShaderNoun = 'OBSAddShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Page Peel, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Based on Shadertoy shader by droozle -// -// Description: -// -// -// Changelog: -// 0.01 - Initial release - -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 50.0; - float step = 0.001; -> = 1.00; -uniform float Position< - string label = "Position"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 0.0; +uniform texture2d other_image; -float4 mainImage( VertData v_in ) : TARGET +float4 mainImage(VertData v_in) : TARGET { - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; - - float t = Position + elapsed_time * Speed; - // Define the fold. - float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - - // Sample texture. - float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - col *= 0.25; // Background color (peeled off). - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - float4 back = image.Sample( textureSampler, pt ); // Back color. - back.rgb = back.rgb * 0.25 + 0.75; - - float shadow = smoothstep( 0.0, 0.2, -side ); - back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); - - // Support for transparency. - col = lerp( col, back.rgb, back.a ); - } - } - - // Output to screen - return float4(col,1.0); -} + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return clamp(base + other, 0.0, 1.0); +} ' } @@ -29079,40 +19438,25 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelTransitionShader { +function Get-OBSAlphaBorderShader { -[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] +[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] param( -# Set the image_a of OBSPagePeelTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSPagePeelTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSPagePeelTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPagePeelTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the page_color of OBSPagePeelTransitionShader -[Alias('page_color')] -[ComponentModel.DefaultBindingProperty('page_color')] +# Set the border_color of OBSAlphaBorderShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] [String] -$PageColor, -# Set the page_transparency of OBSPagePeelTransitionShader -[Alias('page_transparency')] -[ComponentModel.DefaultBindingProperty('page_transparency')] +$BorderColor, +# Set the border_thickness of OBSAlphaBorderShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the alpha_cut_off of OBSAlphaBorderShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] [Single] -$PageTransparency, +$AlphaCutOff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29143,77 +19487,44 @@ $UseShaderTime process { -$shaderName = 'page-peel-transition' -$ShaderNoun = 'OBSPagePeelTransitionShader' +$shaderName = 'alpha_border' +$ShaderNoun = 'OBSAlphaBorderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time< - string label = "Transittion Time"; +uniform float4 border_color< + string label = "Border color"; +> = {0.0,0.0,0.0,1.0}; +uniform int border_thickness< + string label = "Border thickness"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; -uniform float page_transparency< - string label = "Page Transparency"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.001; + float step = 0.01; > = 0.5; float4 mainImage(VertData v_in) : TARGET { - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; - - float t = transition_time * 12.0 + 11.0; - // Define the fold. - float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - - // Sample texture. - float3 col = float3(1.0,0.0,0.0); - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - // Next page - col = image_b.Sample( textureSampler, uv ).rgb; - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - - - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - col = image_a.Sample( textureSampler, pt ).rgb; // Back color. - col = lerp(page_color.rgb, col, page_transparency); - - float shadow = smoothstep( 0.0, 0.2, -side ); - col = lerp( col * 0.2, col, shadow ); - }else{ - col = image_a.Sample( textureSampler, uv ).rgb; - } + float4 pix = image.Sample(textureSampler, v_in.uv); + if (pix.a > alpha_cut_off) + return pix; + [loop] for(int x = -border_thickness;x alpha_cut_off) + return border_color; + } + } } - - // Output to screen - if(convert_linear) - col = srgb_nonlinear_to_linear(col); - return float4(col,1.0); + return pix; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29311,59 +19622,60 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPerlinNoiseShader { +function Get-OBSAlphaGamingBentCameraShader { -[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] +[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] param( -# Set the speed of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('speed')] +# Set the left_side_width of OBSAlphaGamingBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] [Single] -$Speed, -# Set the animated of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the apply_to_channel of OBSPerlinNoiseShader -[Alias('apply_to_channel')] -[ComponentModel.DefaultBindingProperty('apply_to_channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the inverted of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('inverted')] -[Management.Automation.SwitchParameter] -$Inverted, -# Set the multiply of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('multiply')] -[Management.Automation.SwitchParameter] -$Multiply, -# Set the speed_horizonal of OBSPerlinNoiseShader -[Alias('speed_horizonal')] -[ComponentModel.DefaultBindingProperty('speed_horizonal')] +$LeftSideWidth, +# Set the left_side_size of OBSAlphaGamingBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] [Single] -$SpeedHorizonal, -# Set the speed_vertical of OBSPerlinNoiseShader -[Alias('speed_vertical')] -[ComponentModel.DefaultBindingProperty('speed_vertical')] +$LeftSideSize, +# Set the left_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] [Single] -$SpeedVertical, -# Set the iterations of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('iterations')] -[Int32] -$Iterations, -# Set the white_noise of OBSPerlinNoiseShader -[Alias('white_noise')] -[ComponentModel.DefaultBindingProperty('white_noise')] +$LeftSideShadow, +# Set the left_flip_width of OBSAlphaGamingBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] [Single] -$WhiteNoise, -# Set the black_noise of OBSPerlinNoiseShader -[Alias('black_noise')] -[ComponentModel.DefaultBindingProperty('black_noise')] +$LeftFlipWidth, +# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] [Single] -$BlackNoise, -# Set the notes of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$LeftFlipShadow, +# Set the right_side_width of OBSAlphaGamingBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] +[Single] +$RightSideWidth, +# Set the right_side_size of OBSAlphaGamingBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] +[Single] +$RightSideSize, +# Set the right_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] +[Single] +$RightSideShadow, +# Set the right_flip_width of OBSAlphaGamingBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] +[Single] +$RightFlipWidth, +# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29394,257 +19706,127 @@ $UseShaderTime process { -$shaderName = 'perlin_noise' -$ShaderNoun = 'OBSPerlinNoiseShader' +$shaderName = 'alpha-gaming-bent-camera' +$ShaderNoun = 'OBSAlphaGamingBentCameraShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// -// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -// -// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson -// Translation and modification was made by Keijiro Takahashi. -// Conversion for OBS by Charles Fettinger. -// -// This shader is based on the webgl-noise GLSL shader. For further details -// of the original shader, please see the following description from the -// original source code. -// - // -// GLSL textureless classic 2D noise "cnoise", (white_noise) -// with an RSL-style periodic variant "pnoise" (black_noise). -// Author: Stefan Gustavson (stefan.gustavson@liu.se) -// Version: 2011-08-22 -// -// Many thanks to Ian McEwan of Ashima Arts for the -// ideas for permutation and gradient selection. -// -// Copyright (c) 2011 Stefan Gustavson. All rights reserved. -// Distributed under the MIT license. See LICENSE file. -// https://github.com/ashima/webgl-noise -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} - float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} - float4 permute(float4 x) -{ - return mod289(((x*34.0)+1.0)*x); -} - float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} - float2 fade(float2 t) { - return t*t*t*(t*(t*6.0-15.0)+10.0); -} - // Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - // Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - //The good bits~ adapting the noise generator for the plugin and giving some control over the shader - //todo: pseudorandom number generator w/ seed -uniform float speed< - string label = "Speed"; +uniform float left_side_width< + string label = "Left side width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform bool animated; -uniform bool apply_to_channel; -uniform bool inverted; -uniform bool multiply; -uniform float speed_horizonal< - string label = "Speed horizontal"; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform float speed_vertical< - string label = "Speed vertical"; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0; -uniform int iterations< - string label = "Iterations"; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 4; -//how much c_noise do we want? white -uniform float white_noise< - string label = "White noise"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float left_flip_shadow< + string label = "Left flip shadow"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -//how much p_noise do we want? black -uniform float black_noise< - string label = "Black noise"; + float maximum = 1.0; + float step = 0.01; +> = 0.6; + +uniform float right_side_width< + string label = "Right side width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform string notes< - string widget_type = "info"; -> = "white noise and black noise and iterations.. enjoy!"; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; - float2 noisePosition(float t){ - return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); -} - float4 mainImage(VertData v_in) : TARGET +float4 mainImage(VertData v_in) : TARGET { - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - float2 dir = float2(speed_horizonal,speed_vertical); - - if(!animated){ - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = v_in.uv * scale; - float2 period = float2(scale * 2.0, scale * 2.0); - - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } - - //o += pnoise(coord, period) * w; - - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); - } - } else { - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = (v_in.uv + t*dir) * scale; - float2 period = float2(scale * 2.0, scale * 2.0); - - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } - - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); - } - } -} + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; + } + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; + } + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29742,129 +19924,57 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPieChartShader { +function Get-OBSAnimatedPathShader { -[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] +[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] param( -# Set the inner_radius of OBSPieChartShader -[Alias('inner_radius')] -[ComponentModel.DefaultBindingProperty('inner_radius')] -[Single] -$InnerRadius, -# Set the outer_radius of OBSPieChartShader -[Alias('outer_radius')] -[ComponentModel.DefaultBindingProperty('outer_radius')] +# Set the ViewProj of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAnimatedPathShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$OuterRadius, -# Set the start_angle of OBSPieChartShader -[Alias('start_angle')] -[ComponentModel.DefaultBindingProperty('start_angle')] +$ElapsedTime, +# Set the uv_offset of OBSAnimatedPathShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedPathShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedPathShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedPathShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$StartAngle, -# Set the total of OBSPieChartShader -[ComponentModel.DefaultBindingProperty('total')] -[Int32] -$Total, -# Set the part_1 of OBSPieChartShader -[Alias('part_1')] -[ComponentModel.DefaultBindingProperty('part_1')] -[Int32] -$Part1, -# Set the color_1 of OBSPieChartShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the part_2 of OBSPieChartShader -[Alias('part_2')] -[ComponentModel.DefaultBindingProperty('part_2')] -[Int32] -$Part2, -# Set the color_2 of OBSPieChartShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the part_3 of OBSPieChartShader -[Alias('part_3')] -[ComponentModel.DefaultBindingProperty('part_3')] -[Int32] -$Part3, -# Set the color_3 of OBSPieChartShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the part_4 of OBSPieChartShader -[Alias('part_4')] -[ComponentModel.DefaultBindingProperty('part_4')] -[Int32] -$Part4, -# Set the color_4 of OBSPieChartShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] -[String] -$Color4, -# Set the part_5 of OBSPieChartShader -[Alias('part_5')] -[ComponentModel.DefaultBindingProperty('part_5')] -[Int32] -$Part5, -# Set the color_5 of OBSPieChartShader -[Alias('color_5')] -[ComponentModel.DefaultBindingProperty('color_5')] -[String] -$Color5, -# Set the part_6 of OBSPieChartShader -[Alias('part_6')] -[ComponentModel.DefaultBindingProperty('part_6')] -[Int32] -$Part6, -# Set the color_6 of OBSPieChartShader -[Alias('color_6')] -[ComponentModel.DefaultBindingProperty('color_6')] -[String] -$Color6, -# Set the part_7 of OBSPieChartShader -[Alias('part_7')] -[ComponentModel.DefaultBindingProperty('part_7')] -[Int32] -$Part7, -# Set the color_7 of OBSPieChartShader -[Alias('color_7')] -[ComponentModel.DefaultBindingProperty('color_7')] -[String] -$Color7, -# Set the part_8 of OBSPieChartShader -[Alias('part_8')] -[ComponentModel.DefaultBindingProperty('part_8')] -[Int32] -$Part8, -# Set the color_8 of OBSPieChartShader -[Alias('color_8')] -[ComponentModel.DefaultBindingProperty('color_8')] -[String] -$Color8, -# Set the part_9 of OBSPieChartShader -[Alias('part_9')] -[ComponentModel.DefaultBindingProperty('part_9')] -[Int32] -$Part9, -# Set the color_9 of OBSPieChartShader -[Alias('color_9')] -[ComponentModel.DefaultBindingProperty('color_9')] -[String] -$Color9, -# Set the part_10 of OBSPieChartShader -[Alias('part_10')] -[ComponentModel.DefaultBindingProperty('part_10')] +$RandF, +# Set the speed_percent of OBSAnimatedPathShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] [Int32] -$Part10, -# Set the color_10 of OBSPieChartShader -[Alias('color_10')] -[ComponentModel.DefaultBindingProperty('color_10')] +$SpeedPercent, +# Set the path_map of OBSAnimatedPathShader +[Alias('path_map')] +[ComponentModel.DefaultBindingProperty('path_map')] [String] -$Color10, +$PathMap, +# Set the reverse of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29895,160 +20005,100 @@ $UseShaderTime process { -$shaderName = 'pie-chart' -$ShaderNoun = 'OBSPieChartShader' +$shaderName = 'animated_path' +$ShaderNoun = 'OBSAnimatedPathShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float inner_radius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 32.0; -uniform float outer_radius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 50.0; -uniform float start_angle< - string label = "Start angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 90.0; +// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform int total< - string label = "Total"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 100; -uniform int part_1< - string label = "Part 1"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 50; -uniform float4 color_1 = {0.0,0.26,0.62,1.0}; -uniform int part_2< - string label = "Part 2"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 25; -uniform float4 color_2 = {0.24,0.40,0.68,1.0}; -uniform int part_3< - string label = "Part 3"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 10; -uniform float4 color_3 = {0.38,0.56,0.75,1.0}; -uniform int part_4< - string label = "Part 4"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 5; -uniform float4 color_4 = {0.52,0.72,0.81,1.0}; -uniform int part_5< - string label = "Part 5"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 3; -uniform float4 color_5 = {0.69,0.87,0.86,1.0}; -uniform int part_6< - string label = "Part 6"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 2; -uniform float4 color_6 = {1.0,0.79,0.73,1.0}; -uniform int part_7< - string label = "Part 7"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_7 = {0.99,0.57,0.57,1.0}; -uniform int part_8< - string label = "Part 8"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_8 = {0.91,0.36,0.44,1.0}; -uniform int part_9< - string label = "Part 9"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_9 = {0.77,0.16,0.32,1.0}; -uniform int part_10< - string label = "Part 10"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 0; -uniform float4 color_10 = {0.58,0.0,0.23,1.0}; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; + +uniform int speed_percent = 100; +uniform texture2d path_map; +uniform bool reverse = false; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} + +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + float3 pos = v_in.pos.xyz; + float3 current_pos; + float speed = speed_percent * 0.01; + //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float t = 1.0 + sin(elapsed_time * speed) ; + // combine luma texture and user defined shine color + float luma = path_map.Sample(textureSampler, v_in.uv).x; + if (reverse) + { + luma = 1.0 - luma; + } + + float time = lerp(0.0f, 1.0f , t - 1.0); + + // set current position in time + current_pos.x = 0; + current_pos.y = 0; + + + float2 offset = uv_offset; + if (speed == 0.0f) + { + offset.x = 0.0f; + offset.y = 0.0f; + } + else + { + offset.x = uv_offset.x + time * luma; + offset.y = uv_offset.y + time * luma; + } + + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} float4 mainImage(VertData v_in) : TARGET { - const float pi = 3.14159265358979323846; -#ifdef OPENGL - float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); - float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); -#else - float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; - float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; -#endif - float2 center = float2(0.5, 0.5); - float2 factor; - if(uv_size.x < uv_size.y){ - factor = float2(1.0, uv_size.y/uv_size.x); - }else{ - factor = float2(uv_size.x/uv_size.y, 1.0); - } - center = center * factor; - float d = distance(center, v_in.uv * factor); - if(d > outer_radius/100.0 || d < inner_radius/100.0){ - return image.Sample(textureSampler, v_in.uv); - } - float2 toCenter = center - v_in.uv*factor; - float angle = atan2(toCenter.y ,toCenter.x); - angle = angle - (start_angle / 180.0 * pi); - if(angle < 0.0) - angle = pi + pi + angle; - if(angle < 0.0) - angle = pi + pi + angle; - angle = angle / (pi + pi); - float t = 0.0; - for(int i = 0; i < 10; i+=1) { - float part = parts[i]/total; - if(angle > t && angle <= t+part){ - return colors[i]; - } - t = t + part; - } - return image.Sample(textureSampler, v_in.uv); + return image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30146,27 +20196,122 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationShader { +function Get-OBSAnimatedTextureShader { -[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] +[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] param( -# Set the Target_Width of OBSPixelationShader -[Alias('Target_Width')] -[ComponentModel.DefaultBindingProperty('Target_Width')] +# Set the ViewProj of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAnimatedTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$TargetWidth, -# Set the Target_Height of OBSPixelationShader -[Alias('Target_Height')] -[ComponentModel.DefaultBindingProperty('Target_Height')] +$ElapsedTime, +# Set the uv_offset of OBSAnimatedTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$TargetHeight, -# Set the notes of OBSPixelationShader +$RandF, +# Set the uv_size of OBSAnimatedTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSAnimatedTextureShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] +# Set the Animation_Image of OBSAnimatedTextureShader +[Alias('Animation_Image')] +[ComponentModel.DefaultBindingProperty('Animation_Image')] +[String] +$AnimationImage, +# Set the Colorization_Image of OBSAnimatedTextureShader +[Alias('Colorization_Image')] +[ComponentModel.DefaultBindingProperty('Colorization_Image')] +[String] +$ColorizationImage, +# Set the reverse of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# Set the bounce of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('bounce')] +[Management.Automation.SwitchParameter] +$Bounce, +# Set the center_animation of OBSAnimatedTextureShader +[Alias('center_animation')] +[ComponentModel.DefaultBindingProperty('center_animation')] +[Management.Automation.SwitchParameter] +$CenterAnimation, +# Set the polar_animation of OBSAnimatedTextureShader +[Alias('polar_animation')] +[ComponentModel.DefaultBindingProperty('polar_animation')] +[Management.Automation.SwitchParameter] +$PolarAnimation, +# Set the polar_angle of OBSAnimatedTextureShader +[Alias('polar_angle')] +[ComponentModel.DefaultBindingProperty('polar_angle')] +[Single] +$PolarAngle, +# Set the polar_height of OBSAnimatedTextureShader +[Alias('polar_height')] +[ComponentModel.DefaultBindingProperty('polar_height')] +[Single] +$PolarHeight, +# Set the speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] +[Single] +$SpeedHorizontalPercent, +# Set the speed_vertical_percent of OBSAnimatedTextureShader +[Alias('speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] +[Single] +$SpeedVerticalPercent, +# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('tint_speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] +[Single] +$TintSpeedHorizontalPercent, +# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader +[Alias('tint_speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] +[Single] +$TintSpeedVerticalPercent, +# Set the Alpha of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('Alpha')] +[Single] +$Alpha, +# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader +[Alias('Use_Animation_Image_Color')] +[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] +[Management.Automation.SwitchParameter] +$UseAnimationImageColor, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] [String] $SourceName, # The name of the filter. If this is not provided, this will default to the shader name. @@ -30194,53 +20339,169 @@ $UseShaderTime process { -$shaderName = 'pixelation' -$ShaderNoun = 'OBSPixelationShader' +$shaderName = 'animated_texture' +$ShaderNoun = 'OBSAnimatedTextureShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// with help from SkeltonBowTV -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform float Target_Width< - string label = "Target Width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 320.0; -uniform float Target_Height< - string label = "Target Height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 180.0; -uniform string notes< - string widget_type = "info"; -> = "adjust width and height to your screen dimension"; +// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 +// Animates a texture with polar sizing and color options +// for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes; + +uniform texture2d Animation_Image; +uniform texture2d Colorization_Image; +uniform bool reverse = false; +uniform bool bounce = false; +uniform bool center_animation = true; +uniform bool polar_animation = true; +uniform float polar_angle = 90.0; +uniform float polar_height = 1.0; +uniform float speed_horizontal_percent = 50; +uniform float speed_vertical_percent = 5; +uniform float tint_speed_horizontal_percent = 50; +uniform float tint_speed_vertical_percent = 5; +uniform float Alpha = 1.0; +uniform bool Use_Animation_Image_Color = true; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float4 convert_pmalpha(float4 color) +{ + float4 ret = color; + if (color.a >= 0.001) + ret.xyz /= color.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} + +float2 time(float2 speed_dir) +{ + float PI = 3.1415926535897932384626433832795; //acos(-1); + + float2 t = (elapsed_time * speed_dir) ; + if (bounce) + { + // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen + t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; + t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; + t *= -0.5; + } + + if (reverse) + t = t * -1; + return t; +} + +VertData mainTransform(VertData v_in) +{ + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + + VertData vert_out; + //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); + + float2 offset = uv_offset; + + if (center_animation) + { + vert_out.uv = v_in.uv - 0.5f; + } + else + { + offset += time(speed_dir); + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + } + + return vert_out; +} + float4 mainImage(VertData v_in) : TARGET { - float targetWidth = Target_Width; - if(targetWidth < 2.0) - targetWidth = 2.0; - float targetHeight = Target_Height; - if(targetHeight < 2.0) - targetHeight = 2.0; - float2 tex1; - int pixelSizeX = int(uv_size.x / targetWidth); - int pixelSizeY = int(uv_size.y / targetHeight); + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 - int pixelX = int(v_in.uv.x * uv_size.x); - int pixelY = int(v_in.uv.y * uv_size.y); + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); - tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; - tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; + //compensate for background vertex shader values + float2 background_offset = float2(-.5,-.5); + if (!center_animation) + background_offset = time(speed_dir); + float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); - float4 c1 = image.Sample(textureSampler, tex1 ); + // Convert our texture coordinates to polar form: + if (polar_animation) { + + float2 polar = float2( + atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle + log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius + ); - return c1; + // Check how much our texture sampling point changes between + // neighbouring pixels to the sides (ddx) and above/below (ddy) + ///float4 gradient = float4(ddx(polar), ddy(polar)); + + // If our angle wraps around between adjacent samples, + // discard one full rotation from its value and keep the fraction. + ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; + + float2 tintUVs = polar * 4; + tintUVs += time(tint_speed_dir); + + // Apply texture scale + polar *= 4; + // Scroll the texture over time. + polar += time(speed_dir); + float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); + + + float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); + float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); + //animation.a = clamp((intensity),0.0,1.0); + if (Use_Animation_Image_Color) + { + animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + else + { + animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + //if (keyAmount > 0.5f) + rgba = lerp(rgba, animation, animation.a * Alpha); + } + + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } ' @@ -30340,34 +20601,32 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationTransitionShader { +function Get-OBSAsciiShader { -[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] +[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] param( -# Set the transition_time of OBSPixelationTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPixelationTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] +# Set the scale of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('scale')] +[Int32] +$Scale, +# Set the base_color of OBSAsciiShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] +[String] +$BaseColor, +# Set the monochrome of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('monochrome')] [Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the power of OBSPixelationTransitionShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the center_x of OBSPixelationTransitionShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSPixelationTransitionShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, +$Monochrome, +# Set the character_set of OBSAsciiShader +[Alias('character_set')] +[ComponentModel.DefaultBindingProperty('character_set')] +[Int32] +$CharacterSet, +# Set the note of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('note')] +[String] +$Note, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30398,61 +20657,109 @@ $UseShaderTime process { -$shaderName = 'pixelation-transition' -$ShaderNoun = 'OBSPixelationTransitionShader' +$shaderName = 'ascii' +$ShaderNoun = 'OBSAsciiShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = 0.5; - float maximum = 8.0; - float step = 0.01; -> = 3.0; -uniform float center_x< - string label = "X"; - string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Y"; +// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 +// https://github.com/Oncorporation/obs-shaderfilter +// Based on the following shaders: +// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 +// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 + +// Modifications of original shaders include: +// - Porting from GLSL to HLSL +// - Combining characters sets from both source shaders +// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set +// +// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ +// converts a bitmap into int then decodes it to look like text + +uniform int scale< + string label = "Scale"; string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; // Size of characters +uniform float4 base_color< + string label = "Base color"; +> = {0.0,1.0,0.0,1.0}; // Monochrome base color +uniform bool monochrome< + string label = "Monochrome"; +> = false; +uniform int character_set< + string label = "Character set"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Large set of non-letters"; + int option_1_value = 1; + string option_1_label = "Small set of capital letters"; +> = 0; +uniform string note< + string widget_type = "info"; +> = "Base color is used as monochrome base color."; -float4 mainImage(VertData v_in) : TARGET +float character(int n, float2 p) { - //1..0..1 - float scale = abs(transition_time - 0.5) * 2.0; - scale = pow(scale, power); + p = floor(p*float2(4.0, 4.0) + 2.5); + if (clamp(p.x, 0.0, 4.0) == p.x) + { + if (clamp(p.y, 0.0, 4.0) == p.y) + { + int a = int(round(p.x) + 5.0 * round(p.y)); + if (((n >> a) & 1) == 1) return 1.0; + } + } + return 0.0; +} - float2 uv = v_in.uv; - uv -= float2(center_x, center_y); - uv *= uv_size; - uv *= scale; - uv = floor(uv); - uv /= scale; - uv /= uv_size; - uv += float2(center_x, center_y); - uv = clamp(uv, 1.0/uv_size, 1.0); - float4 rgba = image.Sample(textureSampler, uv); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x/y); +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 iResolution = uv_size*uv_scale; + float2 pix = v_in.pos.xy; + float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); + + float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + + int n; + int charset = clamp(character_set, 0, 1); + + if (charset==0) + { + if (gray <= 0.2) n = 4096; // . + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 332772; // * + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 23385164; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13199452; // @ + if (gray > 0.8) n = 11512810; // # + } + else if (charset==1) + { + if (gray <= 0.1) n = 0; + if (gray > 0.1) n = 9616687; // R + if (gray > 0.3) n = 32012382; // S + if (gray > 0.5) n = 16303663; // D + if (gray > 0.7) n = 15255086; // O + if (gray > 0.8) n = 16301615; // B + } + + float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); + + if (monochrome) + { + c.rgb = base_color.rgb; + } + c = c*character(n, p); + + return c; } ' @@ -30552,41 +20859,56 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPolarShader { +function Get-OBSAspectRatioShader { -[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] +[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] param( -# Set the center_x of OBSPolarShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSPolarShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the point_y of OBSPolarShader -[Alias('point_y')] -[ComponentModel.DefaultBindingProperty('point_y')] -[Single] -$PointY, -# Set the flip of OBSPolarShader -[ComponentModel.DefaultBindingProperty('flip')] -[Management.Automation.SwitchParameter] -$Flip, -# Set the rotate of OBSPolarShader -[ComponentModel.DefaultBindingProperty('rotate')] +# Set the ViewProj of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAspectRatioShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$Rotate, -# Set the repeat of OBSPolarShader -[ComponentModel.DefaultBindingProperty('repeat')] +$ElapsedTime, +# Set the uv_offset of OBSAspectRatioShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAspectRatioShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAspectRatioShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAspectRatioShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$Repeat, -# Set the scale of OBSPolarShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, +$RandF, +# Set the uv_size of OBSAspectRatioShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the borderColor of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('borderColor')] +[String] +$BorderColor, +# Set the notes of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30617,84 +20939,103 @@ $UseShaderTime process { -$shaderName = 'polar' -$ShaderNoun = 'OBSPolarShader' +$shaderName = 'aspect_ratio' +$ShaderNoun = 'OBSAspectRatioShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 -#define PI_2 6.2831 -#define mod(x,y) (x - y * floor(x / y)) +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; -uniform float point_y< - string label = "Point y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform bool flip; +// variables +uniform float4 borderColor = {0,0,0,0}; +float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; +uniform string notes; -uniform float rotate< - string label = "Rotate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -uniform float repeat< - string label = "Repeat"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 1.0; +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + + float2 hw = uv_scale; + // determine the game window''s current aspect ratio + float windowaspect = hw.x / hw.y; + + // current viewport height should be scaled by this amount + float scaleheight = windowaspect / targetaspect; + + + // if scaled height is less than current height, add letterbox + if (scaleheight < 1.0f) + { + Rect rect = camera.rect; + + rect.width = 1.0f; + rect.height = scaleheight; + rect.x = 0; + rect.y = (1.0f - scaleheight) / 2.0f; + + camera.rect = rect; + } + else // add pillarbox + { + float scalewidth = 1.0f / scaleheight; + + Rect rect = camera.rect; + + rect.width = scalewidth; + rect.height = 1.0f; + rect.x = (1.0f - scalewidth) / 2.0f; + rect.y = 0; + + camera.rect = rect; + } + return vert_out; +} float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - uv.x -= center_x ; - uv.y -= center_y ; - uv.x = uv.x * ( uv_size.x / uv_size.y); - float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; - if(repeat < 1.0){ - pixel_angle = mod(pixel_angle+rotate,1.0); - if(pixel_angle > repeat) - return float4(0,0,0,0); - pixel_angle = mod(pixel_angle/repeat,1.0); - } else { - pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); - } - float pixel_distance = length(uv)/ scale - point_y; - float2 uv2 = float2(pixel_angle , pixel_distance); - if(flip) - uv2 = float2(1.0,1.0) - uv2; - return image.Sample(textureSampler,uv2); + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30792,62 +21133,78 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPulseShader { +function Get-OBSBackgroundRemovalShader { -[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] +[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] param( -# Set the ViewProj of OBSPulseShader +# Set the ViewProj of OBSBackgroundRemovalShader [ComponentModel.DefaultBindingProperty('ViewProj')] [Single[][]] $ViewProj, -# Set the image of OBSPulseShader +# Set the image of OBSBackgroundRemovalShader [ComponentModel.DefaultBindingProperty('image')] [String] $Image, -# Set the elapsed_time of OBSPulseShader +# Set the elapsed_time of OBSBackgroundRemovalShader [Alias('elapsed_time')] [ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] $ElapsedTime, -# Set the uv_offset of OBSPulseShader +# Set the uv_offset of OBSBackgroundRemovalShader [Alias('uv_offset')] [ComponentModel.DefaultBindingProperty('uv_offset')] [Single[]] $UvOffset, -# Set the uv_scale of OBSPulseShader +# Set the uv_scale of OBSBackgroundRemovalShader [Alias('uv_scale')] [ComponentModel.DefaultBindingProperty('uv_scale')] [Single[]] $UvScale, -# Set the uv_pixel_interval of OBSPulseShader +# Set the uv_pixel_interval of OBSBackgroundRemovalShader [Alias('uv_pixel_interval')] [ComponentModel.DefaultBindingProperty('uv_pixel_interval')] [Single[]] $UvPixelInterval, -# Set the rand_f of OBSPulseShader +# Set the rand_f of OBSBackgroundRemovalShader [Alias('rand_f')] [ComponentModel.DefaultBindingProperty('rand_f')] [Single] $RandF, -# Set the uv_size of OBSPulseShader +# Set the uv_size of OBSBackgroundRemovalShader [Alias('uv_size')] [ComponentModel.DefaultBindingProperty('uv_size')] [Single[]] $UvSize, -# Set the speed of OBSPulseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSPulseShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSPulseShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +# Set the notes of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the target of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('target')] +[String] +$Target, +# Set the color of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the opacity of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('opacity')] [Single] -$MaxGrowthPixels, +$Opacity, +# Set the invert of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the Convert_709to601 of OBSBackgroundRemovalShader +[Alias('Convert_709to601')] +[ComponentModel.DefaultBindingProperty('Convert_709to601')] +[Management.Automation.SwitchParameter] +$Convert709to601, +# Set the Convert_601to709 of OBSBackgroundRemovalShader +[Alias('Convert_601to709')] +[ComponentModel.DefaultBindingProperty('Convert_601to709')] +[Management.Automation.SwitchParameter] +$Convert601to709, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30878,10 +21235,12 @@ $UseShaderTime process { -$shaderName = 'pulse' -$ShaderNoun = 'OBSPulseShader' +$shaderName = 'background_removal' +$ShaderNoun = 'OBSBackgroundRemovalShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' +// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Exeldro February 19, 2022 uniform float4x4 ViewProj; uniform texture2d image; @@ -30891,71 +21250,109 @@ uniform float2 uv_scale; uniform float2 uv_pixel_interval; uniform float rand_f; uniform float2 uv_size; +uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; + +uniform texture2d target; +uniform float4 color; +uniform float opacity = 15.0; +uniform bool invert; +uniform bool Convert_709to601; +uniform bool Convert_601to709; -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 0.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 200.0; sampler_state textureSampler { Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; + AddressU = Clamp; + AddressV = Clamp; }; -struct VertData { +struct VertDataIn { float4 pos : POSITION; float2 uv : TEXCOORD0; }; -VertData mainTransform(VertData v_in) -{ - VertData vert_out; +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; - float3 pos = v_in.pos.xyz; - float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); - float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; - float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; +float dot(float3 a,float3 b){ + return a.x*b.x+a.y*b.y+a.z*b.z; +} - float t = (1 + sin(elapsed_time * speed)) / 2; - float3 current_pos = min_pos * (1 - t) + max_pos * t; +//BT.601 to BT.709 +// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input +// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. +float4 Convert601to709(float4 rgba) +{ + float3 s1 = rgba.rgb; + s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace + return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace +} - vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; +//BT.709 to BT.601 +float4 Convert709to601(float4 rgba) +{ + float3 s1 = rgba.rgb; + s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); + return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; +} + +VertDataOut VSDefault(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); + vert_out.uv = v_in.uv; + vert_out.uv2 = v_in.uv * uv_scale + uv_offset; return vert_out; } -float4 mainImage(VertData v_in) : TARGET +float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET { - return image.Sample(textureSampler, v_in.uv); + float Tolerance = opacity * 0.01; + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; + if (invert){ + targetRGB.rgb = 1.0 - targetRGB.rgb; + } + if (Convert_709to601) + { + rgba.rgb = Convert709to601(rgba).rgb; + targetRGB.rgb = Convert709to601(targetRGB).rgb; + } + + if (Convert_601to709) + { + rgba.rgb = Convert601to709(rgba).rgb; + targetRGB.rbg = Convert601to709(targetRGB).rgb; + } + + float4 shadowRGB = targetRGB * targetRGB; + + if ((abs(targetRGB.r - rgba.r) <= Tolerance && + abs(targetRGB.g - rgba.g) <= Tolerance && + abs(targetRGB.b - rgba.b) <= Tolerance) + || (abs(shadowRGB.r - rgba.r) <= Tolerance && + abs(shadowRGB.g - rgba.g) <= Tolerance && + abs(shadowRGB.b - rgba.b) <= Tolerance)) + { + rgba.rgba = float4(0,0,0,0); + } + return rgba; } technique Draw { pass { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); + vertex_shader = VSDefault(v_in); + pixel_shader = PSColorMaskRGBA(v_in); } } + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -31053,65 +21450,47 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRainbowShader { +function Get-OBSBlendOpacityShader { -[Alias('Set-OBSRainbowShader','Add-OBSRainbowShader')] +[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] param( -# Set the Saturation of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Saturation')] -[Single] -$Saturation, -# Set the Luminosity of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Luminosity')] -[Single] -$Luminosity, -# Set the Spread of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Spread')] -[Single] -$Spread, -# Set the Speed of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Alpha_Percentage of OBSRainbowShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Vertical of OBSRainbowShader +# Set the Vertical of OBSBlendOpacityShader [ComponentModel.DefaultBindingProperty('Vertical')] [Management.Automation.SwitchParameter] $Vertical, -# Set the Rotational of OBSRainbowShader +# Set the Rotational of OBSBlendOpacityShader [ComponentModel.DefaultBindingProperty('Rotational')] [Management.Automation.SwitchParameter] $Rotational, -# Set the Rotation_Offset of OBSRainbowShader +# Set the Rotation_Offset of OBSBlendOpacityShader [Alias('Rotation_Offset')] [ComponentModel.DefaultBindingProperty('Rotation_Offset')] [Single] $RotationOffset, -# Set the Apply_To_Image of OBSRainbowShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSRainbowShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSRainbowShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +# Set the Opacity_Start_Percent of OBSBlendOpacityShader +[Alias('Opacity_Start_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] +[Single] +$OpacityStartPercent, +# Set the Opacity_End_Percent of OBSBlendOpacityShader +[Alias('Opacity_End_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] +[Single] +$OpacityEndPercent, +# Set the Spread of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Spread')] +[Single] +$Spread, +# Set the Speed of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] [Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSRainbowShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Notes of OBSRainbowShader +$ApplyToAlphaLayer, +# Set the Notes of OBSBlendOpacityShader [ComponentModel.DefaultBindingProperty('Notes')] [String] $Notes, @@ -31145,150 +21524,92 @@ $UseShaderTime process { -$shaderName = 'rainbow' -$ShaderNoun = 'OBSRainbowShader' +$shaderName = 'blend_opacity' +$ShaderNoun = 'OBSBlendOpacityShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Rainbow shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 13, 2022 -uniform float Saturation< - string label = "Saturation"; +// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 14, 2022 +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.8; // -uniform float Luminosity< - string label = "Luminosity"; + float maximum = 6.28318531; + float step = 0.01; +> = 0.0; +uniform float Opacity_Start_Percent< + string label = "Opacity Start Percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; // + float maximum = 100.0; + float step = 1.0; +> = 0.0; +uniform float Opacity_End_Percent< + string label = "Opacity End Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; uniform float Spread< string label = "Spread"; string widget_type = "slider"; - float minimum = 0.5; + float minimum = 0.25; float maximum = 10.0; float step = 0.01; -> = 3.8; // +> = 0.5; uniform float Speed< string label = "Speed"; string widget_type = "slider"; float minimum = -10.0; float maximum = 10.0; float step = 0.01; -> = 2.4; // -uniform float Alpha_Percentage< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; // -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.001; -> = 0.0; // -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; +> = 0.0; +uniform bool Apply_To_Alpha_Layer = true; uniform string Notes< string widget_type = "info"; -> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; +> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; -float hueToRGB(float v1, float v2, float vH) { - vH = frac(vH); - if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); - if ((2.0 * vH) < 1.0) return (v2); - if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); - return clamp(v1, 0.0, 1.0); -} +float4 mainImage(VertData v_in) : TARGET +{ + float4 point_color = image.Sample(textureSampler, v_in.uv); + float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; + float4 gray = float4(luminance,luminance,luminance, 1); -float4 HSLtoRGB(float4 hsl) { - float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); - float v1 = 0.0; - float v2 = 0.0; - - if (hsl.y == 0) { - rgb.xyz = hsl.zzz; - } - else { - - if (hsl.z < 0.5) { - v2 = hsl.z * (1 + hsl.y); - } - else { - v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); - } - - v1 = 2.0 * hsl.z - v2; - - rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); - rgb.y = hueToRGB(v1, v2, hsl.x); - rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); - - } - - return rgb; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - - //set colors and direction - float hue = (-1 * lPos.x) / 2.0; + float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); + float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); - if (Rotational && (Vertical == false)) + if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - hue = (lPos.x * cosine + lPos.y * sine) * 0.5; - } + //set opacity and direction + float opacity = (-1 * lPos.x) * 0.5; - if (Vertical && (Rotational == false)) - { - hue = (-1 * lPos.y) * 0.5; - } + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; + } - hue += time; - hue = frac(hue); - float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); - float4 rgba = HSLtoRGB(hsl); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; - float4 luma_color = float4(luma, luma, luma, luma); - if (Replace_Image_Color) - color = luma_color; - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - + if (Vertical && (Rotational == false)) + { + opacity = (-1 * lPos.y) * 0.5; + } + + opacity += time; + opacity = frac(opacity); + point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } - return rgba; + return point_color; } + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -31386,36 +21707,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRainWindowShader { +function Get-OBSBlinkShader { -[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] +[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] param( -# Set the size of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('size')] -[Single] -$Size, -# Set the blurSize of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('blurSize')] -[Single] -$BlurSize, -# Set the trail_strength of OBSRainWindowShader -[Alias('trail_strength')] -[ComponentModel.DefaultBindingProperty('trail_strength')] -[Single] -$TrailStrength, -# Set the trail_color of OBSRainWindowShader -[Alias('trail_color')] -[ComponentModel.DefaultBindingProperty('trail_color')] -[Single] -$TrailColor, -# Set the speed of OBSRainWindowShader +# Set the speed of OBSBlinkShader [ComponentModel.DefaultBindingProperty('speed')] [Single] $Speed, -# Set the debug of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('debug')] -[Management.Automation.SwitchParameter] -$DebugShader, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -31446,247 +21745,25 @@ $UseShaderTime process { -$shaderName = 'rain-window' -$ShaderNoun = 'OBSRainWindowShader' +$shaderName = 'blink' +$ShaderNoun = 'OBSBlinkShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro -// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 -// https://www.shadertoy.com/view/ltffzl -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - -uniform float size< - string label = "Rain Drop Size"; - string widget_type = "slider"; - float minimum = 0.001; - float maximum = 0.5; - float step = 0.01; -> = 0.2; -uniform float blurSize< - string label = "Blur Radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 32.0; // BLUR SIZE (Radius) -uniform float trail_strength< - string label = "Trail Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform float trail_color< - string label = "Trail Color"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 40.0; uniform float speed< string label = "Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 200.0; + float maximum = 100.0; float step = 0.01; -> = 100.0; -uniform bool debug = false; - - -float fract(float v){ - return v - floor(v); -} - -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); -} - -float3 fract3(float3 v){ - return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); -} - -float3 fract4(float4 v){ - return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); -} - - -float3 N13(float p) { - // from DAVE HOSKINS - float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); - p3 += dot(p3, p3.yzx + 19.19); - return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); -} - -float4 N14(float t) { - return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); -} -float N(float t) { - return fract(sin(t*12345.564)*7658.76); -} - -float Saw(float b, float t) { - return smoothstep(0., b, t)*smoothstep(1., b, t); -} - -float2 Drops(float2 uv, float t) { - - float2 UV = uv; - - // DEFINE GRID - uv.y += t*0.8; - float2 a = float2(6., 1.); - float2 grid = a*2.; - float2 id = floor(uv*grid); - - // RANDOM SHIFT Y - float colShift = N(id.x); - uv.y += colShift; - - // DEFINE SPACES - id = floor(uv*grid); - float3 n = N13(id.x*35.2+id.y*2376.1); - float2 st = fract2(uv*grid)-float2(.5, 0); - - // POSITION DROPS - //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 - float x = n.x-.5; - - float y = UV.y*20.; - - float distort = sin(y+sin(y)); - x += distort*(.5-abs(x))*(n.z-.5); - x *= .7; - float ti = fract(t+n.z); - y = (Saw(.85, ti)-.5)*.9+.5; - float2 p = float2(x, y); - - // DROPS - float d = length((st-p)*a.yx); - - float dSize = size; - - float Drop = smoothstep(dSize, .0, d); - - - float r = sqrt(smoothstep(1., y, st.y)); - float cd = abs(st.x-x); - - // TRAILS - float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); - float trailFront = smoothstep(-.02, .02, st.y-y); - trail *= trailFront; - - - // DROPLETS - y = UV.y; - y += N(id.x); - float trail2 = smoothstep(dSize*r, .0, cd); - float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; - y = fract(y*10.)+(st.y-.5); - float dd = length(st-float2(x, y)); - droplets = smoothstep(dSize*N(id.x), 0., dd); - float m = Drop+droplets*r*trailFront; - if(debug){ - m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES - } - - - return float2(m, trail); -} - -float StaticDrops(float2 uv, float t) { - uv *= 30.; - - float2 id = floor(uv); - uv = fract2(uv)-.5; - float3 n = N13(id.x*107.45+id.y*3543.654); - float2 p = (n.xy-.5)*0.5; - float d = length(uv-p); - - float fade = Saw(.025, fract(t+n.z)); - float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; - - return c; -} - -float2 Rain(float2 uv, float t) { - //float s = StaticDrops(uv, t); - float2 r1 = Drops(uv, t); - float2 r2 = Drops(uv*1.8, t); - float c; - if(debug){ - c = r1.x; - }else{ - c = r1.x+r2.x;//s+r1.x+r2.x; - } - - c = smoothstep(.3, 1., c); - - if(debug){ - return float2(c, r1.y); - }else{ - return float2(c, max(r1.y, r2.y)); - } -} +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; - uv.y = 1.0 - uv.y; - uv = uv * uv_scale; - float2 UV = v_in.uv; - float T = elapsed_time * speed / 100.0; - - float t = T*.2; - - UV = (UV-.5)*(.9)+.5; - - float2 c = Rain(uv, t); - - float2 e = float2(.001, 0.); //pixel offset - float cx = Rain(uv+e, t).x; - float cy = Rain(uv+e.yx, t).x; - float2 n = float2(cx-c.x, cy-c.x); //normals - - // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj - float Pi = 6.28318530718; // Pi*2 - - // GAUSSIAN BLUR SETTINGS {{{ - float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) - // GAUSSIAN BLUR SETTINGS }}} - float2 Radius = blurSize/uv_size; - float3 col = image.Sample(textureSampler, UV).rgb; - - if(blurSize > 0.0){ - // Blur calculations - for(float d=0.0; d; -uniform int shadow_offset_y< - string label = "shadow offset y"; +> = 5; // +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; - int minimum = -100; - int maximum = 100; + int minimum = 0; + int maximum = 20; int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; +> = 9; // +uniform float ampFactor< + string label = "amp Factor"; string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 1; - -uniform float4 shadow_color; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 2.0; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; float4 mainImage(VertData v_in) : TARGET { - int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); - float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); - float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); - - float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); - float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); - float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); - - return final_shadow_color * (1-color.a) + color; + int radiusSteps = clamp(Radius_Steps, 0, 20); + int angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + float minRadius = (0.0 * uv_pixel_interval.y); + float maxRadius = (10.0 * uv_pixel_interval.y); + + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 outputPixel = c0; + float4 accumulatedColor = float4(0,0,0,0); + + int totalSteps = radiusSteps * angleSteps; + float angleDelta = (2.0 * PI) / float(angleSteps); + float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); + + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + float(radiusStep) * radiusDelta; + + for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor =image.Sample(textureSampler, currentCoord); + float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); + + accumulatedColor += currentFraction * currentColor / float(totalSteps); + + } + } + + outputPixel += accumulatedColor * ampFactor; + + return outputPixel; } ' @@ -31985,28 +22081,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSReflectShader { +function Get-OBSBorderShader { -[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] +[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] param( -# Set the Horizontal of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the center_x_percent of OBSReflectShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSReflectShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, +# Set the borderColor of OBSBorderShader +[ComponentModel.DefaultBindingProperty('borderColor')] +[String] +$BorderColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32037,63 +22119,24 @@ $UseShaderTime process { -$shaderName = 'Reflect' -$ShaderNoun = 'OBSReflectShader' +$shaderName = 'border' +$ShaderNoun = 'OBSBorderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Reflect Shader - -// Reflects horizontally and/or vertically. - -uniform bool Horizontal< - string label = "Reflect horizontally"; -> = false; -uniform bool Vertical< - string label = "Reflect vertically"; -> = true; - -uniform int center_x_percent< - string label = "center x percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; - +uniform float4 borderColor; float4 mainImage(VertData v_in) : TARGET { - float2 pos = v_in.uv; - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - - if (Horizontal == true) { - if (pos.x < center_pos.x) { - pos.x = center_pos.x - pos.x; - } else if (pos.x == center_pos.x) { - pos.x = pos.x; - } else { - pos.x = pos.x - center_pos.x; - } + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; } - if (Vertical == true) { - if (pos.y < center_pos.y) { - pos.y = center_pos.y - pos.y; - } else if (pos.y == center_pos.y) { - pos.y = pos.y; - } else { - pos.y = pos.y - center_pos.y; - } + else + { + return image.Sample(textureSampler, v_in.uv); } - - return image.Sample(textureSampler, pos); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32191,19 +22234,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRemovePartialPixelsShader { +function Get-OBSBoxBlurShader { -[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] +[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] param( -# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +# Set the Strength of OBSBoxBlurShader +[ComponentModel.DefaultBindingProperty('Strength')] [Int32] -$MinimumAlphaPercent, -# Set the notes of OBSRemovePartialPixelsShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$Strength, +# Set the Mask_Left of OBSBoxBlurShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSBoxBlurShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSBoxBlurShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSBoxBlurShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32234,36 +22292,89 @@ $UseShaderTime process { -$shaderName = 'remove_partial_pixels' -$ShaderNoun = 'OBSRemovePartialPixelsShader' +$shaderName = 'box-blur' +$ShaderNoun = 'OBSBoxBlurShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int minimum_alpha_percent< - string label = "minimum alpha percent"; +uniform int Strength< + string label = "Strength (1)"; string widget_type = "slider"; int minimum = 0; - int maximum = 100; + int maximum = 25; int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; +> = 1; +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; float4 mainImage(VertData v_in) : TARGET { - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - else - { - return float4(output_color); + if(Strength <= 0) + return image.Sample(textureSampler, v_in.uv); + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + float transparent = 0.0; + int count = 1; + float samples = 0.0; + float4 c = float4(0.0, 0.0, 0.0, 0.0); + float Steps = float(Strength); + + [loop] for (int i = -Strength; i <= Strength; i++) { + [loop] for (int k = -Strength; k <= Strength; k++) { + float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); + transparent += sc.a; + count++; + c += sc * sc.a; + samples += sc.a; + } } + if(samples > 0.0) + c /= samples; + + c.a = transparent / float(count); + return c; } ' } @@ -32362,72 +22473,33 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatShader { +function Get-OBSBulgePinchShader { -[Alias('Set-OBSRepeatShader','Add-OBSRepeatShader')] +[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] param( -# Set the ViewProj of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSRepeatShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the radius of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('radius')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$Radius, +# Set the magnitude of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('magnitude')] [Single] -$RandF, -# Set the alpha of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('alpha')] +$Magnitude, +# Set the center_x of OBSBulgePinchShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] [Single] -$Alpha, -# Set the copies of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('copies')] +$CenterX, +# Set the center_y of OBSBulgePinchShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] [Single] -$Copies, -# Set the notes of OBSRepeatShader +$CenterY, +# Set the animate of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the notes of OBSBulgePinchShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -32461,79 +22533,78 @@ $UseShaderTime process { -$shaderName = 'repeat' -$ShaderNoun = 'OBSRepeatShader' +$shaderName = 'BulgePinch' +$ShaderNoun = 'OBSBulgePinchShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 - -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; - -uniform float alpha< - string label = "Alpha"; +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; + float maximum = 2.0; + float step = 0.01; +> = 0.0; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = -1.3333; + float maximum = 1.3333; + float step = 0.01; +> = 0.0; +uniform float center_x< + string label = "Center x"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 4.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform bool animate = false; + uniform string notes< string widget_type = "info"; -> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; - -sampler_state def_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; +> = "Distorts the screen, expanding or drawing in pixels around a point." -struct VertInOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -VertInOut VSDefault(VertInOut vert_in) +float4 mainImage(VertData v_in) : TARGET { - VertInOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv = vert_in.uv * sqrt(copies); - return vert_out; -} + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + v_out.uv = 1. * v_in.uv - center; -float4 PSDrawBare(VertInOut vert_in) : TARGET -{ - float4 rgba = image.Sample(def_sampler, vert_in.uv); - rgba.a *= alpha; - return rgba; -} + center.x /= ar; + v_out.uv.x /= ar; -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); - } -} + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); + float percent = dist/radius; + if(anim_mag > 0) + v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); + else + v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); + v_out.uv += (2 * center); + v_out.uv.x *= ar; + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32631,85 +22702,61 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatTextureShader { +function Get-OBSBurnShader { -[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] +[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] param( -# Set the ViewProj of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatTextureShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatTextureShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatTextureShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the tex_image of OBSRepeatTextureShader -[Alias('tex_image')] -[ComponentModel.DefaultBindingProperty('tex_image')] +# Set the Burn_Gradient of OBSBurnShader +[Alias('Burn_Gradient')] +[ComponentModel.DefaultBindingProperty('Burn_Gradient')] [String] -$TexImage, -# Set the elapsed_time of OBSRepeatTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +$BurnGradient, +# Set the Speed of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Speed')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$Speed, +# Set the Gradient_Adjust of OBSBurnShader +[Alias('Gradient_Adjust')] +[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] [Single] -$RandF, -# Set the blend of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('blend')] +$GradientAdjust, +# Set the Dissolve_Value of OBSBurnShader +[Alias('Dissolve_Value')] +[ComponentModel.DefaultBindingProperty('Dissolve_Value')] [Single] -$Blend, -# Set the copies of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('copies')] +$DissolveValue, +# Set the Animated of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Animated')] +[Management.Automation.SwitchParameter] +$Animated, +# Set the Apply_to_Channel of OBSBurnShader +[Alias('Apply_to_Channel')] +[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] +[Management.Automation.SwitchParameter] +$ApplyToChannel, +# Set the Apply_Smoke of OBSBurnShader +[Alias('Apply_Smoke')] +[ComponentModel.DefaultBindingProperty('Apply_Smoke')] +[Management.Automation.SwitchParameter] +$ApplySmoke, +# Set the Smoke_Horizonal_Speed of OBSBurnShader +[Alias('Smoke_Horizonal_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] [Single] -$Copies, -# Set the notes of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('notes')] +$SmokeHorizonalSpeed, +# Set the Smoke_Vertical_Speed of OBSBurnShader +[Alias('Smoke_Vertical_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] +[Single] +$SmokeVerticalSpeed, +# Set the Iterations of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Iterations')] +[Int32] +$Iterations, +# Set the Notes of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Notes')] [String] $Notes, -# Set the alpha_percentage of OBSRepeatTextureShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Single] -$AlphaPercentage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32740,102 +22787,169 @@ $UseShaderTime process { -$shaderName = 'repeat_texture' -$ShaderNoun = 'OBSRepeatTextureShader' +$shaderName = 'burn' +$ShaderNoun = 'OBSBurnShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 - -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; -uniform texture2d tex_image; +//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Exeldro February 17, 2022 +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} +float4 mod289(float4 x) +{ + return x - floor(x / 289.0) * 289.0; +} +float4 permute(float4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} +float4 taylorInvSqrt(float4 r) +{ + return 1.79284291400159 - r * 0.85373472095314; +} +float2 fade(float2 t) { + return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); +} -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; +float dot(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; +} -uniform float blend< - string label = "Blend"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 4.0; -uniform string notes< - string widget_type = "info"; -> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; -uniform float alpha_percentage< - string label = "alpha percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; +// Classic Perlin noise +float cnoise(float2 P) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} +// Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} -sampler_state tex_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; +uniform texture2d Burn_Gradient = "burngradient.png"; +uniform float Speed = 0.33; +uniform float Gradient_Adjust = 0.85; +uniform float Dissolve_Value = 1.43; +uniform bool Animated; +uniform bool Apply_to_Channel; +uniform bool Apply_Smoke = true; +uniform float Smoke_Horizonal_Speed = 0.3; +uniform float Smoke_Vertical_Speed = 0.17; +uniform int Iterations = 4; +uniform string Notes< + string widget_type = "info"; +> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; -sampler_state base_sampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float4 smoke = float4(1.0,1.0,1.0,1.0); + float4 result = smoke; + float t = elapsed_time * Speed; + float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay + float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); + //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); -struct VertIn { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; + float perlin = 0.5; + float smoke_perlin = 0; + float scale = 1; + float w = 0.5; -struct VertOut { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; + for (int i = 0; i < Iterations; i++) { + //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; + float2 coord = (v_in.uv + t * (dir * .1)) * scale; + float2 period = scale * dir; + perlin += pnoise(coord, period) * w; + if (Apply_Smoke) + smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; -VertOut VSDefault(VertIn vert_in) -{ - VertOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv_1 = vert_in.uv_0; - vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); - return vert_out; -} + scale *= 2.0; + w *= 0.5; + } -float4 PSDrawBare(VertOut vert_in) : TARGET -{ - float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); - float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); - float4 base = image.Sample(base_sampler, vert_in.uv_1); + //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); + if (!Animated) + cycle = 1; + float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); + float overOne = saturate(d * Gradient_Adjust); + float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); - return (1 - alpha) * base + (alpha) * tex; -} + if (Apply_to_Channel) { + result = c * burn; + } + else { + result = float4(perlin, perlin, perlin, 1.0) * burn; + } -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); + if (smoke_perlin > 0) { + smoke *= smoke_perlin; + if (result.a <= 0.04) + result = float4(smoke.rgb, smoke_perlin); + result += smoke; } -} + return result; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32933,26 +23047,67 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGBAPercentShader { +function Get-OBSCartoonShader { -[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] +[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] param( -# Set the RedPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('RedPercent')] -[Single] -$RedPercent, -# Set the GreenPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('GreenPercent')] -[Single] -$GreenPercent, -# Set the BluePercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('BluePercent')] +# Set the ViewProj of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSCartoonShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$BluePercent, -# Set the AlphaPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('AlphaPercent')] +$ElapsedTime, +# Set the uv_offset of OBSCartoonShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSCartoonShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSCartoonShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSCartoonShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$AlphaPercent, +$RandF, +# Set the uv_size of OBSCartoonShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the hue_steps of OBSCartoonShader +[Alias('hue_steps')] +[ComponentModel.DefaultBindingProperty('hue_steps')] +[Int32] +$HueSteps, +# Set the value_steps of OBSCartoonShader +[Alias('value_steps')] +[ComponentModel.DefaultBindingProperty('value_steps')] +[Int32] +$ValueSteps, +# Set the Apply_To_Alpha_Layer of OBSCartoonShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32983,58 +23138,104 @@ $UseShaderTime process { -$shaderName = 'RGBA_Percent' -$ShaderNoun = 'OBSRGBAPercentShader' +$shaderName = 'cartoon' +$ShaderNoun = 'OBSCartoonShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple RGBA Percent Shader -// Allows Red, Green, or Blue to be adjusted between 0-200% -// Allows Alpha to be adjusted between 0/100% -uniform float RedPercent< - string label = "Red percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; +//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float GreenPercent< - string label = "Green percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes = "5/2 seems reasonable"; -uniform float BluePercent< - string label = "Blue percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200; - float step = 1.0; -> = 100.0; +uniform int hue_steps = 5; +uniform int value_steps = 2; +uniform bool Apply_To_Alpha_Layer = true; +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; -uniform float AlphaPercent< - string label = "Alpha percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -float4 mainImage(VertData v_in) : TARGET +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertDataOut VSDefault(VertDataIn v_in) { - float2 pos = v_in.uv; - - float4 imageColors = image.Sample(textureSampler, v_in.uv); - float4 adjustedColor = float4( - imageColors.r * (RedPercent * 0.01), - imageColors.g * (GreenPercent * 0.01), - imageColors.b * (BluePercent * 0.01), - imageColors.a * (AlphaPercent * 0.01) - ); - return adjustedColor; + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float3 rgb2hsv(float3 c) +{ + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} + +float fit(float v, int factor) +{ + return round(v * factor) / factor; +} + +float hue_wrap(float h) +{ + return fmod(h + 1, 2) - 1; + if(h > 1) + return h - 2; + if(h < -1) + return h + 2; + return h; +} + +float4 PassThrough(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float3 hsv = rgb2hsv(rgba.rgb); + hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); + //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); + rgba = float4(hsv2rgb(hsv), rgba.a); + //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); + } + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PassThrough(v_in); + } } ' @@ -33134,54 +23335,28 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbColorWheelShader { +function Get-OBSCellShadedShader { -[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] +[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] param( -# Set the speed of OBSRgbColorWheelShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the color_depth of OBSRgbColorWheelShader -[Alias('color_depth')] -[ComponentModel.DefaultBindingProperty('color_depth')] -[Single] -$ColorDepth, -# Set the Apply_To_Image of OBSRgbColorWheelShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSRgbColorWheelShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSRgbColorWheelShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Alpha_Percentage of OBSRgbColorWheelShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the center_width_percentage of OBSRgbColorWheelShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] +# Set the Angle_Steps of OBSCellShadedShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] [Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRgbColorWheelShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] +$AngleSteps, +# Set the Radius_Steps of OBSCellShadedShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] [Int32] -$CenterHeightPercentage, +$RadiusSteps, +# Set the ampFactor of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('ampFactor')] +[Single] +$AmpFactor, +# Set the notes of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33212,101 +23387,76 @@ $UseShaderTime process { -$shaderName = 'rgb_color_wheel' -$ShaderNoun = 'OBSRgbColorWheelShader' +$shaderName = 'cell_shaded' +$ShaderNoun = 'OBSCellShadedShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 -uniform float speed< - string label = "Speed"; +// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 15.0; - float step = 0.1; -> = 2.0; -uniform float color_depth< - string label = "Color Depth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 2.10; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100; // -uniform int center_width_percentage< - string label = "center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; + int minimum = 1; + int maximum = 20; int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; +> = 5; +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; int minimum = 0; - int maximum = 100; + int maximum = 20; int step = 1; -> = 50; +> = 9; +uniform float ampFactor< + string label = "amp Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 2.0; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; -float3 hsv2rgb(float3 c) +float4 mainImage(VertData v_in) : TARGET { - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (3 * uv_pixel_interval.y); + float maxRadius = (24 * uv_pixel_interval.y); -float mod(float x, float y) -{ - return x - y * floor(x / y); -} + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.14159265f;//acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st ; - float r = length(toCenter) * color_depth; - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(speed ,18)) / 18; + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); - rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - + for (float angle=0; angle <(2*PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps; + + } } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } + accumulatedColor *= ampFactor; - return rgba; + return c0 - accumulatedColor; // Cell shaded style } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -33404,34 +23554,48 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbSplitShader { +function Get-OBSChromaticAberrationShader { -[Alias('Set-OBSRgbSplitShader','Add-OBSRgbSplitShader')] +[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] param( -# Set the redx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redx')] -[Single] -$Redx, -# Set the redy of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redy')] -[Single] -$Redy, -# Set the greenx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greenx')] -[Single] -$Greenx, -# Set the greeny of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greeny')] -[Single] -$Greeny, -# Set the bluex of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluex')] +# Set the power of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('power')] [Single] -$Bluex, -# Set the bluey of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluey')] +$Power, +# Set the gamma of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('gamma')] [Single] -$Bluey, +$Gamma, +# Set the num_iter of OBSChromaticAberrationShader +[Alias('num_iter')] +[ComponentModel.DefaultBindingProperty('num_iter')] +[Int32] +$NumIter, +# Set the distort_radial of OBSChromaticAberrationShader +[Alias('distort_radial')] +[ComponentModel.DefaultBindingProperty('distort_radial')] +[Management.Automation.SwitchParameter] +$DistortRadial, +# Set the distort_barrel of OBSChromaticAberrationShader +[Alias('distort_barrel')] +[ComponentModel.DefaultBindingProperty('distort_barrel')] +[Management.Automation.SwitchParameter] +$DistortBarrel, +# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader +[Alias('offset_spectrum_ycgco')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYcgco, +# Set the offset_spectrum_yuv of OBSChromaticAberrationShader +[Alias('offset_spectrum_yuv')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYuv, +# Set the use_random of OBSChromaticAberrationShader +[Alias('use_random')] +[ComponentModel.DefaultBindingProperty('use_random')] +[Management.Automation.SwitchParameter] +$UseRandom, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33462,64 +23626,214 @@ $UseShaderTime process { -$shaderName = 'rgb_split' -$ShaderNoun = 'OBSRgbSplitShader' +$shaderName = 'chromatic-aberration' +$ShaderNoun = 'OBSChromaticAberrationShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float redx< - string label = "Red X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 2.00; -uniform float redy< - string label = "Red Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greenx< - string label = "Green X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greeny< - string label = "Green Y"; +//based on https://www.shadertoy.com/view/XssGz8 +//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 +uniform float power< + string label = "Power"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; + float minimum = 0.0; + float maximum = 2.0; float step = 0.01; -> = 0.00; -uniform float bluex< - string label = "Blue X"; +> = 0.01; +uniform float gamma< + string label = "Gamma"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; + float minimum = 0.01; + float maximum = 3.0; float step = 0.01; -> = -2.00; -uniform float bluey< - string label = "Blue Y"; +> = 2.2; +uniform int num_iter< + string label = "Number iterations"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; + int minimum = 3; + int maximum = 25; + int step = 1; +> = 7; +uniform bool distort_radial = false; +uniform bool distort_barrel = false; +uniform bool offset_spectrum_ycgco = false; +uniform bool offset_spectrum_yuv = false; +uniform bool use_random = true; + +float2 remap( float2 t, float2 a, float2 b ) { + return clamp( (t - a) / (b - a), 0.0, 1.0 ); +} + +float3 spectrum_offset_rgb( float t ) +{ + float t0 = 3.0 * t - 1.5; + float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); + return ret; +} + + +float3 lin2srgb( float3 c ) +{ + return pow( c, float3(gamma, gamma, gamma) ); +} +float3 srgb2lin( float3 c ) +{ + return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); +} + +float3 yCgCo2rgb(float3 ycc) +{ + float R = ycc.x - ycc.y + ycc.z; + float G = ycc.x + ycc.y; + float B = ycc.x - ycc.y - ycc.z; + return float3(R,G,B); +} + +float3 spectrum_offset_ycgco( float t ) +{ + //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink + //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple + float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange + //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue + return yCgCo2rgb( ygo ); +} + +float3 yuv2rgb( float3 yuv ) +{ + float3 rgb; + rgb.r = yuv.x + yuv.z * 1.13983; + rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); + rgb.b = yuv.x + yuv.y * 2.03211; + return rgb; +} + +float2 radialdistort(float2 coord, float2 amt) +{ + float2 cc = coord - 0.5; + return coord + 2.0 * cc * amt; +} + +float2 barrelDistortion( float2 p, float2 amt ) +{ + p = 2.0 * p - 1.0; + + /* + const float maxBarrelPower = 5.0; + //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper + float theta = atan(p.y, p.x); + float2 radius = float2( length(p) ); + radius = pow(radius, 1.0 + maxBarrelPower * amt); + p.x = radius.x * cos(theta); + p.y = radius.y * sin(theta); + + /*/ + // much faster version + //const float maxBarrelPower = 5.0; + //float radius = length(p); + float maxBarrelPower = sqrt(5.0); + float radius = dot(p,p); //faster but doesn''t match above accurately + p *= pow(float2(radius, radius), maxBarrelPower * amt); + /* */ + + return p * 0.5 + 0.5; +} + +float2 brownConradyDistortion(float2 uv, float dist) +{ + uv = uv * 2.0 - 1.0; + // positive values of K1 give barrel distortion, negative give pincushion + float barrelDistortion1 = 0.1 * dist; // K1 in text books + float barrelDistortion2 = -0.025 * dist; // K2 in text books + + float r2 = dot(uv,uv); + uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; + //uv *= 1.0 + barrelDistortion1 * r2; + + // tangential distortion (due to off center lens elements) + // is not modeled in this function, but if it was, the terms would go here + return uv * 0.5 + 0.5; +} + +float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) +{ + float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); + //float2 dist = mix( min_distort, max_distort, t ); + if (distort_radial) + return radialdistort( uv, 2.0 * dist ); + + if(distort_barrel) + return barrelDistortion( uv, 1.75 * dist ); //distortion at center + return brownConradyDistortion( uv, 75.0 * dist.x ); +} + +// ==== + +float3 spectrum_offset_yuv( float t ) +{ + //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green + //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green + float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange + //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue + return yuv2rgb( yuv ); +} +float3 spectrum_offset( float t ) +{ + if(offset_spectrum_ycgco) + return spectrum_offset_ycgco( t ); + if(offset_spectrum_yuv) + return spectrum_offset_yuv( t ); + return spectrum_offset_rgb( t ); + //return srgb2lin( spectrum_offset_rgb( t ) ); + //return lin2srgb( spectrum_offset_rgb( t ) ); +} float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - if(redx != 0.0 || redy != 0.0) - c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; - if(greenx != 0.0 || greeny != 0.0) - c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; - if(bluex != 0.0 || bluey != 0.0) - c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; - return c; + float2 max_distort = float2(power, power); + float2 min_distort = 0.5 * max_distort; + + float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); + + float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); + + //debug oversiz + //float2 distuv = distort( uv, 1.0, max_distort ); + //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) + //{ + // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; + //} + + + float stepsiz = 1.0 / (float(num_iter)-1.0); + float rnd = 0.0; + if(use_random) + rnd = rand_f; + + float t = rnd * stepsiz; + + float3 sumcol = float3(0.0, 0.0, 0.0); + float3 sumw = float3(0.0, 0.0, 0.0); + float colA = 0.0; + + for ( int i=0; i = 2.2; - -uniform float Green< - string label = "Green"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; +//based on https://www.shadertoy.com/view/WsdyRN -uniform float Blue< - string label = "Blue"; +//Higher values = less distortion +uniform float distortion< + string label = "Distortion"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; + float minimum = 5.0; + float maximum = 1000.0; float step = 0.01; -> = 2.2; - -uniform float RedVisibility< - string label = "Red Visibility"; +> = 75.; +//Higher values = tighter distortion +uniform float amplitude< + string label = "Amplitude"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; + float maximum = 100.0; float step = 0.01; -> = 1.0; - -uniform float GreenVisibility< - string label = "Green Visibility"; +> = 10.; +//Higher values = more color distortion +uniform float chroma< + string label = "Chroma"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; + float maximum = 6.28318531; float step = 0.01; -> = 1.0; +> = .5; -uniform float BlueVisibility< - string label = "Blue Visibility"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; - -uniform string notes< - string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction."; +float2 zoomUv(float2 uv, float zoom) { + float2 uv1 = uv; + uv1 += .5; + uv1 += zoom/2.-1.; + uv1 /= zoom; + return uv1; +} float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - float redChannel = pow(c.r, Red) * RedVisibility; - float greenChannel = pow(c.g, Green) * GreenVisibility; - float blueChannel = pow(c.b, Blue) * BlueVisibility; + float2 uvt = v_in.uv; - return float4(redChannel, greenChannel, blueChannel, c.a); -} + float2 uvtR = uvt; + float2 uvtG = uvt; + float2 uvtB = uvt; + + //Uncomment the following line to get varying chroma distortion + //chroma = sin(elapsed_time)/2.+.5; + + uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); + uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); + uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); + + float2 uvR = zoomUv(uvtR, 1.1); + float2 uvG = zoomUv(uvtG, 1.1); + float2 uvB = zoomUv(uvtB, 1.1); + + float4 colR = image.Sample(textureSampler, uvR); + float4 colG = image.Sample(textureSampler, uvG); + float4 colB = image.Sample(textureSampler, uvB); + return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -33845,22 +24139,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGSSAAShader { +function Get-OBSCircleMaskFilterShader { -[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] +[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] param( -# Set the ColorSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('ColorSigma')] -[Single] -$ColorSigma, -# Set the SpatialSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('SpatialSigma')] +# Set the Radius of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Radius')] [Single] -$SpatialSigma, -# Set the notes of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$Radius, +# Set the Circle_Offset_X of OBSCircleMaskFilterShader +[Alias('Circle_Offset_X')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] +[Int32] +$CircleOffsetX, +# Set the Circle_Offset_Y of OBSCircleMaskFilterShader +[Alias('Circle_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] +[Int32] +$CircleOffsetY, +# Set the Source_Offset_X of OBSCircleMaskFilterShader +[Alias('Source_Offset_X')] +[ComponentModel.DefaultBindingProperty('Source_Offset_X')] +[Int32] +$SourceOffsetX, +# Set the Source_Offset_Y of OBSCircleMaskFilterShader +[Alias('Source_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] +[Int32] +$SourceOffsetY, +# Set the Antialiasing of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Antialiasing')] +[Management.Automation.SwitchParameter] +$Antialiasing, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33891,154 +24201,85 @@ $UseShaderTime process { -$shaderName = 'RGSSAA' -$ShaderNoun = 'OBSRGSSAAShader' +$shaderName = 'circle-mask-filter' +$ShaderNoun = 'OBSCircleMaskFilterShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 -// https://github.com/exeldro/obs-shaderfilter/tree/master -// Using edge detection shader as a base, created by Hallatore -// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 +// Circle Mask Filter version 1.01, for OBS Shaderfilter +// Copyright 2022 by SkeletonBow +// Twitter: +// Twitch: +// License: GNU GPLv2 +// +// Changelog: +// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. +// 1.0 - Initial release -uniform float ColorSigma< - string label = "Color Sigma"; +uniform float Radius< + string label = "Radius"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 1.0; - float step = 0.1; -> = 1.0; - -uniform float SpatialSigma< - string label = "Spatial Sigma"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform int Circle_Offset_X< + string label = "Circle Offset X"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 1.0; - float step = 0.1; -> = 1.0; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Circle_Offset_Y< + string label = "Circle Offset X"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Source_Offset_X< + string label = "Source Offset X"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; +uniform int Source_Offset_Y< + string label = "Source Offset Y"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; -uniform string notes< - string widget_type = "info"; -> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." +uniform bool Antialiasing = true; +#define Smoothness 100.00 +#define AAwidth 4 -float Luminance(float3 rgb) -{ - return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; -} +#define uv_pi uv_pixel_interval -float4 mainImage(VertData v_in) : TARGET +float4 mainImage( VertData v_in ) : TARGET { - float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; - float2 SceneUV = v_in.uv; - float2 TexelScale = 1.0f/uv_size; - - const float SQRT2 = 1.4142135624; - const float PI = 3.141592654; - const float angle = PI / 8.0; - const float cs = cos(angle); - const float sn = sin(angle); - - // Rotated grid samples - float3 C1 = - image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; - float3 C2 = - image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; - float3 C3 = - image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; - float3 C4 = - image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; - float3 C5 = - image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; - float3 C6 = - image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; - float3 C7 = - image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; - float3 C8 = - image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; - - // Luminance edge detection - float A0 = Luminance(SceneColor); - float CL1 = Luminance(C1); - float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); - float CL2 = Luminance(C2); - float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); - float CL3 = Luminance(C3); - float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); - float CL4 = Luminance(C4); - float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); - float CL5 = Luminance(C5); - float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); - float CL6 = Luminance(C6); - float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); - float CL7 = Luminance(C7); - float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); - float CL8 = Luminance(C8); - float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); - float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); - - // Calculate distance-based weights - float2 Dist1 = float2(cs, -sn); - float2 Dist2 = float2(-cs, -sn); - float2 Dist3 = float2(-sn, cs); - float2 Dist4 = float2(sn, cs); - float2 Dist5 = float2(cs * SQRT2, 0); - float2 Dist6 = float2(0, sn * SQRT2); - float2 Dist7 = float2(-cs * SQRT2, 0); - float2 Dist8 = float2(0, -sn * SQRT2); - float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); - float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); - float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); - float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); - float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); - float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); - float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); - float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); - - // Color weights - float3 ColorDiff1 = SceneColor.rgb - C1; - float3 ColorDiff2 = SceneColor.rgb - C2; - float3 ColorDiff3 = SceneColor.rgb - C3; - float3 ColorDiff4 = SceneColor.rgb - C4; - float3 ColorDiff5 = SceneColor.rgb - C5; - float3 ColorDiff6 = SceneColor.rgb - C6; - float3 ColorDiff7 = SceneColor.rgb - C7; - float3 ColorDiff8 = SceneColor.rgb - C8; - - float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); - float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); - float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); - float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); - float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); - float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); - float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); - float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); - - // Mixing weights - float W1 = SW1 * CW1; - float W2 = SW2 * CW2; - float W3 = SW3 * CW3; - float W4 = SW4 * CW4; - float W5 = SW5 * CW5; - float W6 = SW6 * CW6; - float W7 = SW7 * CW7; - float W8 = SW8 * CW8; - float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; - - // Weighted color - float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + - C7 * W7 + C8 * W8) / - max(TotalWeight, 0.0001); + float2 uv = v_in.uv; + float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; + float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; - // Blend it - float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); - float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); - float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); - float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); - float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); - float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); - float HDREdge = max(A0Max, A0Max2); - float EdgeMask = saturate(1.0f - HDREdge); + float radius = Radius * 0.01; + float smwidth = radius * Smoothness * 0.01; + + float4 obstex = image.Sample( textureSampler, uv - soffset); + float4 color = obstex; + // Account for aspect ratio + uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; + float2 cuv = 0.5 + coffset; + float dist = distance(cuv,uv); + // Anti-aliased or pixelated edge + if( Antialiasing ) { + color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); + } else { + color.a = step( dist, radius ); + } - return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); + return float4(color.rgb, color.a); } ' @@ -34138,35 +24379,65 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRippleShader { +function Get-OBSClockAnalogShader { -[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] +[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] param( -# Set the distance_factor of OBSRippleShader -[Alias('distance_factor')] -[ComponentModel.DefaultBindingProperty('distance_factor')] -[Single] -$DistanceFactor, -# Set the time_factor of OBSRippleShader -[Alias('time_factor')] -[ComponentModel.DefaultBindingProperty('time_factor')] -[Single] -$TimeFactor, -# Set the power_factor of OBSRippleShader -[Alias('power_factor')] -[ComponentModel.DefaultBindingProperty('power_factor')] -[Single] -$PowerFactor, -# Set the center_pos_x of OBSRippleShader -[Alias('center_pos_x')] -[ComponentModel.DefaultBindingProperty('center_pos_x')] -[Single] -$CenterPosX, -# Set the center_pos_y of OBSRippleShader -[Alias('center_pos_y')] -[ComponentModel.DefaultBindingProperty('center_pos_y')] -[Single] -$CenterPosY, +# Set the current_time_ms of OBSClockAnalogShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] +[Int32] +$CurrentTimeMs, +# Set the current_time_sec of OBSClockAnalogShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockAnalogShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockAnalogShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the hour_handle_color of OBSClockAnalogShader +[Alias('hour_handle_color')] +[ComponentModel.DefaultBindingProperty('hour_handle_color')] +[Single[]] +$HourHandleColor, +# Set the minute_handle_color of OBSClockAnalogShader +[Alias('minute_handle_color')] +[ComponentModel.DefaultBindingProperty('minute_handle_color')] +[Single[]] +$MinuteHandleColor, +# Set the second_handle_color of OBSClockAnalogShader +[Alias('second_handle_color')] +[ComponentModel.DefaultBindingProperty('second_handle_color')] +[Single[]] +$SecondHandleColor, +# Set the outline_color of OBSClockAnalogShader +[Alias('outline_color')] +[ComponentModel.DefaultBindingProperty('outline_color')] +[Single[]] +$OutlineColor, +# Set the top_line_color of OBSClockAnalogShader +[Alias('top_line_color')] +[ComponentModel.DefaultBindingProperty('top_line_color')] +[Single[]] +$TopLineColor, +# Set the background_color of OBSClockAnalogShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] +[Single[]] +$BackgroundColor, +# Set the time_offset_hours of OBSClockAnalogShader +[Alias('time_offset_hours')] +[ComponentModel.DefaultBindingProperty('time_offset_hours')] +[Int32] +$TimeOffsetHours, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34197,83 +24468,197 @@ $UseShaderTime process { -$shaderName = 'ripple' -$ShaderNoun = 'OBSRippleShader' +$shaderName = 'clock_analog' +$ShaderNoun = 'OBSClockAnalogShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float distance_factor< - string label = "distance factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 12.0; -uniform float time_factor< - string label = "time factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform float power_factor< - string label = "power factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 3.0; -uniform float center_pos_x< - string label = "center pos x"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float center_pos_y< - string label = "center pos y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +//Based on https://www.shadertoy.com/view/XdKXzy +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform float3 hour_handle_color = {1.0,1.0,1.0}; +uniform float3 minute_handle_color = {1.0,1.0,1.0}; +uniform float3 second_handle_color = {1.0,0.0,0.0}; +uniform float3 outline_color = {1.0,1.0,1.0}; +uniform float3 top_line_color = {1.0,0.0,0.0}; +uniform float3 background_color = {.5,.5,.5}; +uniform int time_offset_hours = 0; -float4 mainImage(VertData v_in) : TARGET +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif +// this is my first try to actually use glsl almost from scratch +// so far all i''ve done is learning by doing / reading glsl docs. +// this is inspired by my non glsl „elapsed_time“ projects +// especially this one: https://www.gottz.de/analoguhr.htm + +// i will most likely use a buffer in future to calculate the elapsed_time +// aswell as to draw the background of the clock only once. +// tell me if thats a bad idea. + +// update: +// screenshot: http://i.imgur.com/dF0nHDk.png +// as soon as i think its in a usefull state i''ll release the source +// of that particular c++ application on github. +// i hope sommeone might find it usefull :D + +#define PI 3.141592653589793238462643383 + +// from https://www.shadertoy.com/view/4s3XDn <3 +float ln(float2 p, float2 a, float2 b) { - float2 cPos = (v_in.uv * 2 ) -1; - float2 center_pos = float2(center_pos_x, center_pos_y); - float cLength = distance(cPos, center_pos); - float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; - return image.Sample(textureSampler, uv); + float2 pa = p - a; + float2 ba = b - a; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h); } -' + +// i think i should spend some elapsed_time reading docs in order to minimize this. +// hints apreciated +// (Rotated LiNe) +float rln(float2 uv, float start, float end, float perc) { + float inp = perc * PI * 2.0; + float2 coord = float2(sin(inp), cos(inp)); + return ln(uv, coord * start, coord * end); } -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' + +// i need this to have an alphachannel in the output +// i intend to use an optimized version of this shader for a transparent desktop widget experiment +float4 mixer(float4 c1, float4 c2) { + // please tell me if you think this would boost performance. + // the elapsed_time i implemented mix myself it sure did reduce + // the amount of operations but i''m not sure now + // if (c2.a <= 0.0) return c1; + // if (c2.a >= 1.0) return c2; + return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); + // in case you are curious how you could implement mix yourself: + // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); } -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } + +float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { + if (dist <= width + shadow) { + // lets draw the shadow + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); + // now lets draw the antialiased handle + color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); } - 'Remove' { + return color; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 R = uv_size; + // calculate the size of a pixel + float px = 1.0 / R.y; + // create percentages of the coordinate system + float2 p = (v_in.uv * uv_size).xy / R; + // center the scene and add perspective + float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); + + /*float2 uv = -1.0 + 2.0 * p.xy; + // lets add perspective for mobile device support + if (uv_size.x > uv_size.y) + uv.x *= uv_size.x / uv_size.y; + else + uv.y *= uv_size.y / uv_size.x;*/ + + // lets scale the scene a bit down: + uv *= 1.1; + px *= 0.9; + + float width = 0.015; + float dist = 1.0; + float centerdist = length(uv); + + float4 color = image.Sample(textureSampler, v_in.uv); + + // background of the clock + if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); + + float isRed = 1.0; + + if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { + // minute bars + for (float i = 0.0; i <= 15.0; i += 1.0) { + if (mod(i, 5.0) == 0.0) { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); + // draw first bar red + if (i == 0.0 && uv.y > 0.0) { + isRed = dist; + dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); + color = mixer(color, float4(top_line_color, dist)); + dist = 1.0; + } + } + else { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); + } + } + + // outline circle + dist = min(dist, abs(1.0-width-length(uv))); + // draw clock shadow + if (centerdist > 1.0) + color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); + + // draw outline + minute bars in white + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); + color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); + } + + if (centerdist < 1.0) { + float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); + // hour + color = styleHandle(color, px, + rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), + hour_handle_color, 0.03, 0.02); + + // minute + color = styleHandle(color, px, + rln(uv, -0.075, 0.7, elapsed_time / 3600.0), + minute_handle_color, 0.02, 0.02); + + // second + color = styleHandle(color, px, + min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), + second_handle_color, 0.01, 0.02); + } + + + return color; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { if ($SourceName) { Get-OBSInput | Where-Object InputName -eq $SourceName | @@ -34342,38 +24727,53 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatingSourceShader { +function Get-OBSClockDigitalLedShader { -[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] +[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] param( -# Set the spin_speed of OBSRotatingSourceShader -[Alias('spin_speed')] -[ComponentModel.DefaultBindingProperty('spin_speed')] -[Single] -$SpinSpeed, -# Set the rotation of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('rotation')] -[Single] -$Rotation, -# Set the zoomin of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('zoomin')] -[Single] -$Zoomin, -# Set the keep_aspectratio of OBSRotatingSourceShader -[Alias('keep_aspectratio')] -[ComponentModel.DefaultBindingProperty('keep_aspectratio')] +# Set the current_time_sec of OBSClockDigitalLedShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalLedShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalLedShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the showMatrix of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showMatrix')] [Management.Automation.SwitchParameter] -$KeepAspectratio, -# Set the x_center of OBSRotatingSourceShader -[Alias('x_center')] -[ComponentModel.DefaultBindingProperty('x_center')] -[Single] -$XCenter, -# Set the y_center of OBSRotatingSourceShader -[Alias('y_center')] -[ComponentModel.DefaultBindingProperty('y_center')] -[Single] -$YCenter, +$ShowMatrix, +# Set the showOff of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showOff')] +[Management.Automation.SwitchParameter] +$ShowOff, +# Set the ampm of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ampm')] +[Management.Automation.SwitchParameter] +$Ampm, +# Set the ledColor of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ledColor')] +[String] +$LedColor, +# Set the offsetHours of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34404,85 +24804,214 @@ $UseShaderTime process { -$shaderName = 'rotating-source' -$ShaderNoun = 'OBSRotatingSourceShader' +$shaderName = 'clock_digital_led' +$ShaderNoun = 'OBSClockDigitalLedShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//spin speed higher the slower -uniform float spin_speed< - string label = "Spin Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float rotation< - string label = "Rotation"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.1; -> = 0.0; -uniform float zoomin< - string label = "Zoom"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform bool keep_aspectratio = true; -uniform float x_center< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float y_center< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; +// based on https://www.shadertoy.com/view/MdfGzf +// cmarangu has linked all 7 segments in his comments +// https://www.shadertoy.com/view/3dtSRj +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif + +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; + +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; + +uniform bool showMatrix = false; +uniform bool showOff = false; +uniform bool ampm = false; +uniform float4 ledColor = {1.0,0,0,1.0}; +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; + +float segment(float2 uv, bool On) +{ + if (!On && !showOff) + return 0.0; + + float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* + (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); + + //Fiddle with lights and matrix + //uv.x += sin(elapsed_time*60.0*6.26)/14.0; + //uv.y += cos(elapsed_time*60.0*6.26)/14.0; + + //led like brightness + if (On){ + seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; + } else { + seg *= -(0.05+length(uv*float2(0.2,0.1))); + } + return seg; +} + +float sevenSegment(float2 uv,int num) +{ + float seg= 0.0; + seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); + seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); + seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); + seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); + seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); + seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); + seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); + + return seg; +} + +float showNum(float2 uv,int nr, bool zeroTrim) +{ + //Speed optimization, leave if pixel is not in segment + if (abs(uv.x)>1.5 || abs(uv.y)>1.2) + return 0.0; + + float seg= 0.0; + if (uv.x>0.0) + { + nr /= 10; + if (nr==0 && zeroTrim) + nr = -1; + seg += sevenSegment(uv+float2(-0.75,0.0),nr); + } else { + seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); + } + + return seg; +} + +float dots(float2 uv) +{ + float seg = 0.0; + uv.y -= 0.5; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + uv.y += 1.0; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + return seg; +} -//main fragment code -//from lioran to nutella with love float4 mainImage(VertData v_in) : TARGET { - float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; - float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; - //get position on of the texture and focus on the middle - float i_rotation; - if (spin_speed == 0){ - //turn angle number into pi number - i_rotation = rotation/57.295779513; - }else{ - //use elapsed time for spinning if spin speed is not 0 - i_rotation = elapsed_time * spin_speed; - } - float2 i_point; - i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); - i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); - - //get the angle from center , returns pi number - float i_dir = atan(i_point.y/i_point.x); - if(i_point.x < 0.0){ - i_dir += 3.14159265359; - } - - //get the distance from the centers - float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); - //multiple distance by the zoomin value - i_distance *= zoomin; - - //shift the texture position based on angle and distance from the middle - i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; - i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; - - //draw normally from new point - return image.Sample(textureSampler, i_point); + float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / + min(uv_size.x,uv_size.y); + + if (uv_size.x>uv_size.y) + { + uv *= 6.0; + } + else + { + uv *= 12.0; + } + + uv.x *= -1.0; + uv.x += uv.y/12.0; + //wobble + //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; + //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; + uv.x += 3.5; + float seg = 0.0; + + if(timeMode == 0){ + seg += showNum(uv,current_time_sec,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + seg += showNum(uv,current_time_min,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + if (ampm) { + if(current_time_hour == 0){ + seg += showNum(uv,12,true); + }else if(current_time_hour > 12){ + seg += showNum(uv,current_time_hour-12,true); + }else{ + seg += showNum(uv,current_time_hour,true); + } + } else { + seg += showNum(uv,current_time_hour,true); + } + }else{ + float timeSecs = 0.0; + if(timeMode == 1){ + timeSecs = elapsed_time_enable; + }else if(timeMode == 2){ + timeSecs = elapsed_time_active; + }else if(timeMode == 3){ + timeSecs = elapsed_time_show; + }else if(timeMode == 4){ + timeSecs = elapsed_time_start; + } + + timeSecs += offsetSeconds + offsetHours*3600; + if(timeSecs < 0) + timeSecs = 0.9999-timeSecs; + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + + uv.x -= 1.75; + + seg += dots(uv); + + uv.x -= 1.75; + + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + if (ampm) + { + if(timeSecs == 0.0){ + timeSecs = 12.0; + }else if (timeSecs > 12.0){ + timeSecs = mod(timeSecs,12.0); + } + }else if (timeSecs > 24.0) { + timeSecs = mod(timeSecs,24.0); + } + + uv.x -= 1.75; + + seg += dots(uv); + + uv.x -= 1.75; + seg += showNum(uv,int(mod(timeSecs,60.0)),true); + } + + + if (seg==0.0){ + return image.Sample(textureSampler, v_in.uv); + } + // matrix over segment + if (showMatrix) + { + seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); + //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); + } + if (seg<0.0) + { + seg = -seg;; + return float4(seg,seg,seg,1.0); + } + return float4(ledColor.rgb * seg, ledColor.a); } ' } @@ -34581,102 +25110,62 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatoeShader { +function Get-OBSClockDigitalNixieShader { -[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] +[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] param( -# Set the ViewProj of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSRotatoeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSRotatoeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRotatoeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRotatoeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSRotatoeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSRotatoeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the speed_percent of OBSRotatoeShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] +# Set the current_time_ms of OBSClockDigitalNixieShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] [Int32] -$SpeedPercent, -# Set the Axis_X of OBSRotatoeShader -[Alias('Axis_X')] -[ComponentModel.DefaultBindingProperty('Axis_X')] -[Single] -$AxisX, -# Set the Axis_Y of OBSRotatoeShader -[Alias('Axis_Y')] -[ComponentModel.DefaultBindingProperty('Axis_Y')] -[Single] -$AxisY, -# Set the Axis_Z of OBSRotatoeShader -[Alias('Axis_Z')] -[ComponentModel.DefaultBindingProperty('Axis_Z')] -[Single] -$AxisZ, -# Set the Angle_Degrees of OBSRotatoeShader -[Alias('Angle_Degrees')] -[ComponentModel.DefaultBindingProperty('Angle_Degrees')] -[Single] -$AngleDegrees, -# Set the Rotate_Transform of OBSRotatoeShader -[Alias('Rotate_Transform')] -[ComponentModel.DefaultBindingProperty('Rotate_Transform')] -[Management.Automation.SwitchParameter] -$RotateTransform, -# Set the Rotate_Pixels of OBSRotatoeShader -[Alias('Rotate_Pixels')] -[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] -[Management.Automation.SwitchParameter] -$RotatePixels, -# Set the Rotate_Colors of OBSRotatoeShader -[Alias('Rotate_Colors')] -[ComponentModel.DefaultBindingProperty('Rotate_Colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the center_width_percentage of OBSRotatoeShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] +$CurrentTimeMs, +# Set the current_time_sec of OBSClockDigitalNixieShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] [Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRotatoeShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalNixieShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] [Int32] -$CenterHeightPercentage, -# Set the notes of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalNixieShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the offsetHours of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, +# Set the corecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('corecolor')] +[Single[]] +$Corecolor, +# Set the halocolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('halocolor')] +[Single[]] +$Halocolor, +# Set the flarecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('flarecolor')] +[Single[]] +$Flarecolor, +# Set the anodecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodecolor')] +[Single[]] +$Anodecolor, +# Set the anodehighlightscolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] +[Single[]] +$Anodehighlightscolor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34707,491 +25196,483 @@ $UseShaderTime process { -$shaderName = 'rotatoe' -$ShaderNoun = 'OBSRotatoeShader' +$shaderName = 'clock_digital_nixie' +$ShaderNoun = 'OBSClockDigitalNixieShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; - -uniform int speed_percent< - string label = "speed percentage"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 50; // -uniform float Axis_X< - string label = "Axis X"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.1; -> = 0.0; -uniform float Axis_Y< - string label = "Axis Y"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 0.0; -uniform float Axis_Z< - string label = "Axis Z"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float Angle_Degrees< - string label = "Angle Degrees"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 0.01; -> = 45.0; -uniform bool Rotate_Transform = true; -uniform bool Rotate_Pixels = false; -uniform bool Rotate_Colors = false; -uniform int center_width_percentage< - string label = "center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; - -uniform string notes< - string widget_type = "info"; -> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +//based on https://www.shadertoy.com/view/fsBcRm +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; -float3x3 rotAxis(float3 axis, float a) { - float s=sin(a); - float c=cos(a); - float oc=1.0-c; +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; - float3 as=axis*s; +// Colors as named variables, if you want to tweak them +uniform float3 corecolor = {1.0,0.7,0.0}; +uniform float3 halocolor = {1.0,0.5,0.0}; +uniform float3 flarecolor = {1.0,0.3,0.0}; +uniform float3 anodecolor = {0.2,0.1,0.1}; +uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; - float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); - float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); - return p*oc+q; -} +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#define lessThan(a,b) (a < b) +#define greaterThan(a,b) (a > b) +#endif -VertData mainTransform(VertData v_in) +// psrdnoise (c) Stefan Gustavson and Ian McEwan, +// ver. 2021-12-02, published under the MIT license: +// https://github.com/stegu/psrdnoise/ +float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) { - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; - - // use matrix to transform rotation - if (Rotate_Transform) - vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; - - vert_out.uv = v_in.uv * uv_scale + uv_offset; - - return vert_out; + float2 uv = float2(x.x+x.y*0.5, x.y); + float2 i0 = floor(uv), f0 = frac(uv); + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + float2 i1 = i0 + o1, i2 = i0 + 1.0; + float2 v0 = float2(i0.x - i0.y*0.5, i0.y); + float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); + float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); + float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; + float3 iu, iv, xw, yw; + if(any(greaterThan(period, float2(0.0,0.0)))) { + xw = float3(v0.x, v1.x, v2.x); + yw = float3(v0.y, v1.y, v2.y); + if(period.x > 0.0) + xw = mod(float3(v0.x, v1.x, v2.x), period.x); + if(period.y > 0.0) + yw = mod(float3(v0.y, v1.y, v2.y), period.y); + iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); + } else { + iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); + } + float3 hash = mod(iu, 289.0); + hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); + hash = mod((hash*34.0 + 10.0)*hash, 289.0); + float3 psi = hash*0.07482 + alpha; + float3 gx = cos(psi); float3 gy = sin(psi); + float2 g0 = float2(gx.x, gy.x); + float2 g1 = float2(gx.y, gy.y); + float2 g2 = float2(gx.z, gy.z); + float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); + w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; + float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); + float n = dot(w4, gdotx); + float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; + float2 dn0 = w4.x*g0 + dw.x*x0; + float2 dn1 = w4.y*g1 + dw.y*x1; + float2 dn2 = w4.z*g2 + dw.z*x2; + gradient = 10.9*(dn0 + dn1 + dn2); + return 10.9*n; } -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; +// Compute the shortest distance from p +// to a line segment from p1 to p2. +float lined(float2 p1, float2 p2, float2 p) { + float2 p1p2 = p2 - p1; + float2 v = normalize(p1p2); + float2 s = p - p1; + float t = dot(v, s); + if (t<0.0) return length(s); + if (t>length(p1p2)) return length(p - p2); + return length(s - t*v); +} +// Compute the shortest distance from p to a circle +// with center at c and radius r. (Extremely simple.) +float circled(float2 c, float r, float2 p) { + return abs(length(p - c) - r); +} - // use matrix to transform pixels - if (Rotate_Pixels) - { - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); - } - if (Rotate_Colors) - rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; +// Compute the shortest distance from p to a +// circular arc with center c from p1 to p2. +// p1, p2 are in the +angle direction (ccw), +// to resolve the major/minor arc ambiguity, so +// specifying p1, p2 in the wrong order will +// yield the complement to the arc you wanted. +// If p1 = p2, the entire circle is drawn, but +// you don''t want to use this function to draw +// a circle. Use the simple circled() instead. +// If p1 and p2 have different distances to c, +// the end of the arc will not look right. If +// this is inconvenient, uncomment the 3rd line. +float arcd(float2 c, float2 p1, float2 p2, float2 p) { - return rgba; -} + float2 v1 = p1 - c; + float2 v2 = p2 - c; + // Optional: make sure p1, p2 are both on the circle + // v2 = normalize(v2)*length(v1); + float2 v = p - c; -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} + float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi + if(all(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); // nearest end } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + return abs(length(v) - length(v1)); // dist to arc } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + } else { // Arc angle > pi + if(any(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); + } else { + return abs(length(v) - length(v1)); } + } +} - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +// A convenient anti-aliased step() using auto derivatives +float aastep(float threshold, float value) { + float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); + return smoothstep(threshold-afwidth, threshold+afwidth, value); +} - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +// A smoothstep() that blends to an aastep() under minification +float aasmoothstep(float t1, float t2, float v) { + float aw = 0.7 * length(float2(ddx(v), ddy(v))); + float sw = max(0.5*(t2-t1), aw); + float st = 0.5*(t1+t2); + return smoothstep(st-sw, st+sw, v); +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } +// Distance field of a hexagonal (simplex) grid +// The return vector contains the distances to the +// three closest points, sorted by magnitude. +float3 hexgrid(float2 p) { - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } + const float stretch = 1.0/0.8660; + + // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) + p.y = p.y * stretch; + // Transform to grid space (axis-aligned "simplex" grid) + float2 uv = float2(p.x + p.y*0.5, p.y); + // Determine which simplex we''re in, with i0 being the "base" + float2 i0 = floor(uv); + float2 f0 = frac(uv); + // o1 is the offset in simplex space to the second corner + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + // Enumerate the remaining simplex corners + float2 i1 = i0 + o1; + float2 i2 = i0 + float2(1.0, 1.0); + // Transform corners back to texture space + float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); + float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); + float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); + float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); + // Only three values - bubble sort is just fine. + d.yz = (d.y < d.z) ? d.yz : d.zy; + d.xy = (d.x < d.y) ? d.xy : d.yx; + d.yz = (d.y < d.z) ? d.yz : d.zy; + return d; +} - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } +// The digits. Simple functions, only a lot of them. - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } - } +// These glyphs and their implementation as distance fields +// are the original work of me (stefan.gustavson@gmail.com), +// and the code below is released under the MIT license: +// https://opensource.org/licenses/MIT +// (If that is inconvenient for you, let me know. I''m reasonable.) +// +// Experts say mortals should not attempt to design character shapes. +// "It''s just ten simple digits", I thought, "How hard can it be?" +// A week later, after countless little tweaks to proportions and +// curvature, and with a notepad full of sketches and pen-and-paper +// math, some of it horribly wrong because it was decades since I +// solved this kind of equations by hand, I know the answer: +// It can be *really* hard. But also loads of fun! +// +float nixie0(float2 p) { + // Special hack instead of pasting together arcs and lines + float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); + return abs(d - 2.0); } +float nixie1(float2 p) { + float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); + float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); + return min(d1, d2); } +float nixie1alt(float2 p) { // Straight line + return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); +} -} +float nixie2(float2 p) { + const float x = 3.2368345; // Icky coordinates, + const float y = 4.4283002; // used twice below + float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); + float d2 = arcd(float2(10.657842, -5.001899), // Also icky + float2(x, y), float2(-0.25, 0.0), p); + float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRect2Shader { +float nixie2alt(float2 p) { // Straight neck + float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); + float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); + float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} -[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] -param( -# Set the corner_radius of OBSRoundedRect2Shader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedRect2Shader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRect2Shader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRect2Shader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRect2Shader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRect2Shader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# Set the faster_scan of OBSRoundedRect2Shader -[Alias('faster_scan')] -[ComponentModel.DefaultBindingProperty('faster_scan')] -[Management.Automation.SwitchParameter] -$FasterScan, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +float nixie3(float2 p) { + // Two round parts: + // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); + // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); + // Angled top, more like classic Nixie tube digits: + float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); + float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); + return min(min(d1, d2), d3); +} +float nixie3alt(float2 p) { // Two round parts of the same size + float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); + float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); + return min(d1, d2); +} -process { -$shaderName = 'rounded_rect2' -$ShaderNoun = 'OBSRoundedRect2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Aplha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -uniform bool faster_scan = true; +float nixie4(float2 p) { + // This digit is 5.0 units wide, most others are 4.0 or 4.5 + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); + return min(min(d1, d2), d3); +} + +float nixie4alt(float2 p) { + // This digit is 4.0 units wide, but looks cropped + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(d1, d2), d3); +} + +float nixie5(float2 p) { + float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); + float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); + float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); + float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + return min(min(d1, d2), min(d3, d4)); +} + +float nixie5alt(float2 p) { + float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); + float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); + float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); + float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); + float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); + float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); +} + +float nixie6(float2 p) { + float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); + float d2 = circled(float2(2.0, 2.25), 2.25, p); + return min(d1, d2); +} + +float nixie6alt(float2 p) { // Straight neck + float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); + float d2 = circled(float2(2.0,2.0), 2.0, p); + return min(d1, d2); +} + +float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact + float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); + return min(min(d1, d2), d3); +} + +float nixie7alt(float2 p) { // Straight neck + float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); + return min(d1, d2); +} + +float nixie8(float2 p) { + float d1 = circled(float2(2.0, 2.2), 2.2, p); + float d2 = circled(float2(2.0, 6.2), 1.8, p); + return min(d1, d2); +} + +float nixie8alt(float2 p) { // Same size loops + float d1 = circled(float2(2.0,2.0), 2.0, p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} + +float nixie9(float2 p) { + float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); + float d2 = circled(float2(2.0, 5.75), 2.25, p); + return min(d1, d2); +} + +float nixie9alt(float2 p) { // Straight neck + float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} + +float nixieminus(float2 p) { + return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); +} + +float nixieequals(float2 p) { + float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); + float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); + return min(d1, d2); +} + +float nixieplus(float2 p) { + float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); + float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); + return min(d1, d2); +} + +float nixiedot(float2 p) { + // circled with r=0 yields a point, but with more work + return length(p - float2(2.0, 0.0)); +} + +float nixiecolon(float2 p) { + float d1 = length(p - float2(2.0,2.0)); + float d2 = length(p - float2(2.0, 5.0)); + return min(d1, d2); +} + +// End of MIT-licensed code +float number(int n, float2 p) { + switch(n) { + case 0: return nixie0(p); + case 1: return nixie1(p); + case 2: return nixie2(p); + case 3: return nixie3(p); + case 4: return nixie4(p); + case 5: return nixie5(p); + case 6: return nixie6(p); + case 7: return nixie7(p); + case 8: return nixie8(p); + case 9: return nixie9(p); + default: return 1e10; + } +} +// Display the current time with a retro Nixie-tube look +// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 +// All code in the "Image" tab is public domain. +// Functions in the "Common" tab are also public domain, +// except where a separate license is specified. float4 mainImage(VertData v_in) : TARGET { - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - float check_dist = float(corner_radius); - if(border_thickness > check_dist) - check_dist = border_thickness; - if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(-check_dist); - } - if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(-check_dist); - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(!faster_scan || closedEdgeX != 0){ - [loop] for(int x = 1;float(x) check_dist && border_thickness > corner_radius){ - if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ - float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(floor(cd) > corner_radius) - return float4(0.0,0.0,0.0,0.0); - if(cd > corner_radius){ - d = border_thickness + cd - corner_radius; - } else if(d > border_thickness){ - d = border_thickness; - } - }else if(d > border_thickness){ - d = border_thickness; - } - } - if(floor(d) <= check_dist){ - if(border_thickness > 0){ - if(ceil(check_dist-d) <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - if(border_alpha_start < border_alpha_end){ - fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; - fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); - } - if(d > check_dist) - fade_color.a *= 1.0 -(d - check_dist); - return fade_color; - }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ - float4 fade_color = border_color; - float f; - if(border_thickness > (check_dist-d)) - f = border_thickness - (check_dist-d); - else - f = 1.0 -((check_dist-d) - border_thickness); - fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; - return fade_color; - } - } - if (d > check_dist) - pixel.a *= 1.0 - (d - check_dist); - return pixel; - + float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), + max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); + + // Some relief for the GPU: exit early if we''re in the black margins + if(bbox == 0.0) { + return float4(float3(0.0,0.0,0.0),1.0); } - return float4(0.0,0.0,0.0,0.0); + + // If not, well, let''s put that GPU to good use! + float secs = floor(mod(time, 60.0)); + float mins = floor(mod(time, 3600.0)/60.0); + float hrs = floor(time/3600.0); + int h10 = int(floor(hrs/10.0)); + int h1 = int(floor(mod(hrs, 10.0))); + int m10 = int(floor(mins/10.0)); + int m1 = int(floor(mod(mins, 10.0))); + int s10 = int(floor(secs/10.0)); + int s1 = int(floor(mod(secs, 10.0))); + + float2 wspace = float2(6.5, 0.0); + float2 nspace = float2(3.5, 0.0); + float d = 1e10; + d = min(d, number(h10, p)); + d = min(d, number(h1, p-wspace)); + d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); + d = min(d, number(m10, p-2.0*wspace-nspace)); + d = min(d, number(m1, p-3.0*wspace-nspace)); + d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); + d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); + d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); + + float2 g; // For gradients returned from psrdnoise() + + // Digit outlines + float core = 1.0-aastep(0.2, d); + // "flare" is a wide blurry region around the characters, and + // "flarenoise" is a spatio-temporal modulation of its extents + // (slight flickering, but not all characters at the same time) + float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); + float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); + flare *= flare; // A more rapid decline towards the edge + // "glow" is a variation in the intensity of the glowy cathode (core) + float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); + // Now we mess up the distance field a little for the "halo" effect + d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); + d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); + // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube + // doesn''t have this, but it adds some appealing visual detail. + // Looks more like hot filaments than "cold cathodes", but... oh, well. + float halo = 1.0-smoothstep(-0.3, 0.3, d); + + // Brittle parameters! This scale/shift of p has a strong impact + // on the pattern at the edges of the grid through "anodefade". + float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); + float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders + // Fade the hexagonal holes in the anode towards the edges + float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), + max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); + float anode = 1.0 - aastep(0.1, anodedist - anodefade); + + float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); + float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know + + float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black + mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); + mixcolor = lerp(mixcolor, halocolor, 0.9*halo); + mixcolor = lerp(mixcolor, corecolor, core*glow); + mixcolor = lerp(mixcolor, anodecolorresult, anode); + mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box + return float4(mixcolor,1.0); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -35289,55 +25770,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectPerCornerShader { +function Get-OBSColorDepthShader { -[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] +[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] param( -# Set the corner_radius_tl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tl')] -[ComponentModel.DefaultBindingProperty('corner_radius_tl')] -[Int32] -$CornerRadiusTl, -# Set the corner_radius_tr of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tr')] -[ComponentModel.DefaultBindingProperty('corner_radius_tr')] -[Int32] -$CornerRadiusTr, -# Set the corner_radius_br of OBSRoundedRectPerCornerShader -[Alias('corner_radius_br')] -[ComponentModel.DefaultBindingProperty('corner_radius_br')] -[Int32] -$CornerRadiusBr, -# Set the corner_radius_bl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_bl')] -[ComponentModel.DefaultBindingProperty('corner_radius_bl')] -[Int32] -$CornerRadiusBl, -# Set the border_thickness of OBSRoundedRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] +# Set the colorDepth of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('colorDepth')] [Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +$ColorDepth, +# Set the pixelSize of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('pixelSize')] [Single] -$AlphaCutOff, +$PixelSize, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35368,185 +25812,40 @@ $UseShaderTime process { -$shaderName = 'rounded_rect_per_corner' -$ShaderNoun = 'OBSRoundedRectPerCornerShader' +$shaderName = 'color-depth' +$ShaderNoun = 'OBSColorDepthShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_radius_tl< - string label = "Corner radius top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int corner_radius_tr< - string label = "Corner radius top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int corner_radius_br< - string label = "Corner radius bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int corner_radius_bl< - string label = "Corner radius bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; +//based on https://www.shadertoy.com/view/tscfWM +uniform float colorDepth< + string label = "Color depth"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; + float minimum = 0.01; + float maximum = 100.0; + float step = 0.01; +> = 5.0; + +uniform float pixelSize< + string label = "Pixel Size"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.01; +> = 5.0; + float4 mainImage(VertData v_in) : TARGET { - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; - int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; - int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; - int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_radius_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_radius_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_radius_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(d = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; + +uniform texture2d lut< + string label = "LUT"; >; -uniform int corner_radius_left< - string label = "Corner radius left"; +uniform int lut_amount_percent< + string label = "LUT amount percentage"; string widget_type = "slider"; - int minimum = 0; + int minimum = -200; int maximum = 200; int step = 1; ->; -uniform int corner_radius_top< - string label = "Corner radius top"; +> = 100; +uniform int lut_scale_percent< + string label = "LUT scale percentage"; string widget_type = "slider"; - int minimum = 0; + int minimum = -200; int maximum = 200; int step = 1; ->; -uniform int corner_radius_right< - string label = "Corner radius right"; +> = 100; +uniform int lut_offset_percent< + string label = "LUT offset percentage"; string widget_type = "slider"; - int minimum = 0; + int minimum = -200; int maximum = 200; int step = 1; ->; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; +> = 0; + float4 mainImage(VertData v_in) : TARGET { - float4 output_color = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(output_color.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return output_color; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x corner_radius){ - closedEdgeXabs = 0; - } - if(closedEdgeYabs > corner_radius){ - closedEdgeYabs = 0; - } - if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ - return output_color; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; - } - } + float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); + float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); + float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); - float closest = closedEdgeXabs +// Twitch: +// +// Based on: +// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm +// https://www.youtube.com/c/InigoQuilez +// https://iquilezles.org/ +// and the derivative StreamFX Corner Pin effect by Xaymar +// https://github.com/Xaymar/obs-StreamFX +// +// Description: +// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily +// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying +// image with proper perspective without distortion. +// +// TODO: +// - Add feature to automatically quantize corners to pixel centers +// +// Changelog: +// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by +// Inigo Quilez that it was based upon. + +uniform bool Antialias_Edges = true; + +uniform float Top_Left_X< + string label = "Top left x"; string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int border_thickness< - string label = "border thickness"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Top_Left_Y< + string label = "Top left y"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Top_Right_X< + string label = "Top right x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Top_Right_Y< + string label = "Top right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_X< + string label = "Bottom left x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_Y< + string label = "Bottom left y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom right x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; -uniform float4 border_color; +// DEVELOPMENTAL DEBUGGING OPTIONS +//uniform float AAstrength = 1.0; +//uniform float AAdist = 1.0; +//uniform float debug_psmult = 1.0; +//#define PIXEL_SIZE_MULT 2.0 -float4 mainImage(VertData v_in) : TARGET +float cross2d(in float2 a, in float2 b) { - float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); - float4 output_color = image.Sample(textureSampler, v_in.uv); - - float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); - float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); - - bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; - bool is_within_radius = pixel_distance_from_center <= corner_radius; - - bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); - bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); - - return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); + 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; +} + +// distance to a line segment +float sdSegment( in float2 p, in float2 a, in float2 b ) +{ + p -= a; b -= a; + return length( p-b*saturate(dot(p,b)/dot(b,b)) ); +} + +// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) +float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) +{ + //float ps = 2.0 * (2.0/uv_size.y); // Original +// float ps = debug_psmult * (2.0/uv_size.y); + float ps = (2.0/uv_size.y); +// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now + return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); +} + +float4 mainImage( VertData v_in ) : TARGET { + float2 p = 2.* v_in.uv - 1.; + + float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; + float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; + float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + // Convert from screen coords to potential Quad UV coordinates + float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); + + if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { + return float4(0.0, 0.0, 0.0, 0.0); + } + + float4 texel = image.Sample(textureSampler, uv); + + if ( Antialias_Edges ) { + // Anti-alias edges of texture + texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); + } + return texel; } ' @@ -36163,54 +26531,22 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeGradientShader { +function Get-OBSCrtCurvatureShader { -[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] +[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] param( -# Set the corner_radius of OBSRoundedStrokeGradientShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeGradientShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the rotation_speed of OBSRoundedStrokeGradientShader -[Alias('rotation_speed')] -[ComponentModel.DefaultBindingProperty('rotation_speed')] -[Int32] -$RotationSpeed, -# Set the border_colorL of OBSRoundedStrokeGradientShader -[Alias('border_colorL')] -[ComponentModel.DefaultBindingProperty('border_colorL')] -[String] -$BorderColorL, -# Set the border_colorR of OBSRoundedStrokeGradientShader -[Alias('border_colorR')] -[ComponentModel.DefaultBindingProperty('border_colorR')] -[String] -$BorderColorR, -# Set the center_width of OBSRoundedStrokeGradientShader -[Alias('center_width')] -[ComponentModel.DefaultBindingProperty('center_width')] -[Int32] -$CenterWidth, -# Set the center_height of OBSRoundedStrokeGradientShader -[Alias('center_height')] -[ComponentModel.DefaultBindingProperty('center_height')] -[Int32] -$CenterHeight, -# Set the notes of OBSRoundedStrokeGradientShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the strength of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the border of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('border')] [String] -$Notes, +$Border, +# Set the feathering of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('feathering')] +[Single] +$Feathering, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36241,257 +26577,109 @@ $UseShaderTime process { -$shaderName = 'rounded_stroke_gradient' -$ShaderNoun = 'OBSRoundedStrokeGradientShader' +$shaderName = 'crt-curvature' +$ShaderNoun = 'OBSCrtCurvatureShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int rotation_speed< - string label = "rotation speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 border_colorL; -uniform float4 border_colorR; -//uniform float color_spread = 2.0; -uniform int center_width< - string label = "center width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height< - string label = "center height"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 200.; + float step = 0.01; +> = 33.33; -// float3 hsv2rgb(float3 c) -// { -// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); -// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); -// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -// } +uniform float4 border< + string label = "Border Color"; +> = {0., 0., 0., 1.}; -float mod(float x, float y) +uniform float feathering< + string label = "Feathering"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.33; + + +float4 mainImage(VertData v_in) : TARGET { - return x - y * floor(x/y); + float2 cc = v_in.uv - float2(0.5, 0.5); + float dist = dot(cc, cc) * strength / 100.0; + float2 bent = v_in.uv + cc * (1.0 + dist) * dist; + if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { + return border; + } + if (feathering >= .01) { + float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; + float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); + borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; + float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); + return lerp(border, image.Sample(textureSampler, bent), borderFade); + } + + return image.Sample(textureSampler, bent); } -float4 gradient(float c) { - c = mod(c , 2.0); - if(c < 0.0f){ - c = c * -1.0; +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } } - if(c > 1.0){ - c = 1.0 - c; - if(c < 0.0f){ - c = c + 1.0; + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } } - return lerp(border_colorL, border_colorR, c); -} + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } -float4 getBorderColor(float2 toCenter){ - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; - return gradient((angle / 3.14159265f) + angleMod); -} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 st = v_in.uv * uv_scale; - float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); - float2 toCenter = center_pixel_coordinates - st; - - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return output_color; - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - return float4(0.0, 0.0, 0.0, 0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { @@ -36530,34 +26718,37 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeShader { +function Get-OBSCubeRotatingShader { -[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] +[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] param( -# Set the corner_radius of OBSRoundedStrokeShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +# Set the images of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('images')] [Int32] -$MinimumAlphaPercent, -# Set the border_color of OBSRoundedStrokeShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +$Images, +# Set the speed of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the shadow of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('shadow')] +[Single] +$Shadow, +# Set the other_image1 of OBSCubeRotatingShader +[Alias('other_image1')] +[ComponentModel.DefaultBindingProperty('other_image1')] [String] -$BorderColor, -# Set the notes of OBSRoundedStrokeShader -[ComponentModel.DefaultBindingProperty('notes')] +$OtherImage1, +# Set the other_image2 of OBSCubeRotatingShader +[Alias('other_image2')] +[ComponentModel.DefaultBindingProperty('other_image2')] [String] -$Notes, +$OtherImage2, +# Set the other_image3 of OBSCubeRotatingShader +[Alias('other_image3')] +[ComponentModel.DefaultBindingProperty('other_image3')] +[String] +$OtherImage3, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36588,140 +26779,84 @@ $UseShaderTime process { -$shaderName = 'rounded_stroke' -$ShaderNoun = 'OBSRoundedStrokeShader' +$shaderName = 'cube_rotating' +$ShaderNoun = 'OBSCubeRotatingShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; ->; -uniform int border_thickness< - string label = "border thickness"; +uniform int images< + string label = "Images"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "1"; + int option_1_value = 2; + string option_1_label = "2"; + int option_2_value = 4; + string option_2_label = "4"; +> = 1; + +uniform float speed< + string label = "Speed"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; + +uniform float shadow< + string label = "Shadow"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float4 border_color; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; + float minimum = 0.0; + float maximum = 2.5; + float step = 0.001; +> = 1.0; +uniform texture2d other_image1; +uniform texture2d other_image2; +uniform texture2d other_image3; + +#define PI 3.14159265359 float4 mainImage(VertData v_in) : TARGET { - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return float4(output_color); - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return border_color; - } - else - { - return float4(output_color); - } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return border_color; - } - else - { - return float4(output_color); - } - } - - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return border_color; - } - else - { - return output_color; - } - } - return float4(0.0, 0.0, 0.0, 0.0); + float t = elapsed_time * speed; + float4 c = float4(0,0,0,0); + for(float side = 0.0; side<4.0; side += 1.0){ + float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; + float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; + if(left < right){ + float2 uv; + uv.x = (v_in.uv.x-left)/(right-left); + float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; + float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; + float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); + uv.y = (v_in.uv.y-0.5)*size+0.5; + float4 sample = float4(0,0,0,0); + if(images <= 1 || side == 0.0){ + sample = image.Sample(textureSampler, uv); + }else if(images == 2){ + if(side == 1.0 || side == 3.0){ + sample = other_image1.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + }else if(images == 4){ + if(side == 1.0){ + sample = other_image1.Sample(textureSampler, uv); + }else if(side == 2.0){ + sample = other_image2.Sample(textureSampler, uv); + }else if(side == 3.0){ + sample = other_image3.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + } + if(sample.a > 0.0){ + c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); + } + } + } + return c; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36819,46 +26954,23 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScanLineShader { +function Get-OBSCurveShader { -[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] +[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] param( -# Set the lengthwise of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('lengthwise')] -[Management.Automation.SwitchParameter] -$Lengthwise, -# Set the animate of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the speed of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the angle of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the shift of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('shift')] -[Management.Automation.SwitchParameter] -$Shift, -# Set the boost of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('boost')] -[Management.Automation.SwitchParameter] -$Boost, -# Set the floor of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('floor')] +# Set the strength of OBSCurveShader +[ComponentModel.DefaultBindingProperty('strength')] [Single] -$Floor, -# Set the period of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('period')] +$Strength, +# Set the scale of OBSCurveShader +[ComponentModel.DefaultBindingProperty('scale')] [Single] -$Period, -# Set the notes of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('notes')] +$Scale, +# Set the curve_color of OBSCurveShader +[Alias('curve_color')] +[ComponentModel.DefaultBindingProperty('curve_color')] [String] -$Notes, +$CurveColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36889,106 +27001,51 @@ $UseShaderTime process { -$shaderName = 'scan_line' -$ShaderNoun = 'OBSScanLineShader' +$shaderName = 'curve' +$ShaderNoun = 'OBSCurveShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Scan Line Effect for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -//Count the number of scanlines we want via height or width, adjusts the sin wave period -uniform bool lengthwise; -//Do we want the scanlines to move? -uniform bool animate; -//How fast do we want those scanlines to move? -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10000.0; - float step = 1; -> = 1000; -//What angle should the scanlines come in at (based in degrees) -uniform float angle< - string label = "angle"; +#define PI 3.14159265359 + +uniform float strength< + string label = "Strength"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 45; -//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit -//By default values for color range from 0 to 1 -//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen -uniform bool shift = true; -uniform bool boost = true; -//Increases the minimum value of the sin wave -uniform float floor< - string label = "Floor"; + float maximum = 1.0; + float step = 0.001; +> = 0.5; + +uniform float scale< + string label = "Scale"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.001; -> = 0.0; -//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input -uniform float period< - string label = "Period"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 1.0; -> = 10.0; -uniform string notes< - string widget_type = "info"; -> = "floor affects the minimum opacity of the scan line"; +> = 1.0; + +uniform float4 curve_color = {0,0,0,0}; + float4 mainImage(VertData v_in) : TARGET { - //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 - // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 - float nfloor = clamp(floor, 0.0, 100.0) * 0.01; - float nperiod = max(period, 1.0); - float gap = 1 - nfloor; - float pi = 3.1415926535897932384626; - float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); - float nspeed = 0.0; - if(animate){ - nspeed = speed * 0.0001; - } - - float4 color = image.Sample(textureSampler, v_in.uv); - - float t = elapsed_time * nspeed; - - if(!lengthwise){ - float base_height = 1.0 / uv_pixel_interval.y; - float h_interval = pi * base_height; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } - else{ - float base_width = 1.0 / uv_pixel_interval.x; - float w_interval = pi * base_width; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } + float2 uv = v_in.uv; + const float ydiff = 1.0; + uv -= float2(0.5,ydiff); + uv.x *= ( uv_size.x / uv_size.y); + uv /= scale; + if(strength > 0.0){ + float d = tan((1.0-strength)*PI/2.0); + float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); + float2 center = float2(0.0,(1.0-ydiff)+d); + float pd = distance(uv, center); + if(pd < r) + return curve_color; + float angle = atan2(center.x-uv.x,center.y-uv.y); + uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); + } + uv.x /= ( uv_size.x / uv_size.y); + uv += float2(0.5,ydiff); + return image.Sample(textureSampler,uv); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -37086,54 +27143,55 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeascapeShader { +function Get-OBSCutRectPerCornerShader { -[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] +[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] param( -# Set the AA of OBSSeascapeShader -[ComponentModel.DefaultBindingProperty('AA')] -[Management.Automation.SwitchParameter] -$AA, -# Set the SEA_HEIGHT of OBSSeascapeShader -[Alias('SEA_HEIGHT')] -[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] -[Single] -$SEAHEIGHT, -# Set the SEA_CHOPPY of OBSSeascapeShader -[Alias('SEA_CHOPPY')] -[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] -[Single] -$SEACHOPPY, -# Set the SEA_SPEED of OBSSeascapeShader -[Alias('SEA_SPEED')] -[ComponentModel.DefaultBindingProperty('SEA_SPEED')] -[Single] -$SEASPEED, -# Set the SEA_FREQ of OBSSeascapeShader -[Alias('SEA_FREQ')] -[ComponentModel.DefaultBindingProperty('SEA_FREQ')] -[Single] -$SEAFREQ, -# Set the SEA_BASE of OBSSeascapeShader -[Alias('SEA_BASE')] -[ComponentModel.DefaultBindingProperty('SEA_BASE')] -[String] -$SEABASE, -# Set the SEA_WATER_COLOR of OBSSeascapeShader -[Alias('SEA_WATER_COLOR')] -[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] +# Set the corner_tl of OBSCutRectPerCornerShader +[Alias('corner_tl')] +[ComponentModel.DefaultBindingProperty('corner_tl')] +[Int32] +$CornerTl, +# Set the corner_tr of OBSCutRectPerCornerShader +[Alias('corner_tr')] +[ComponentModel.DefaultBindingProperty('corner_tr')] +[Int32] +$CornerTr, +# Set the corner_br of OBSCutRectPerCornerShader +[Alias('corner_br')] +[ComponentModel.DefaultBindingProperty('corner_br')] +[Int32] +$CornerBr, +# Set the corner_bl of OBSCutRectPerCornerShader +[Alias('corner_bl')] +[ComponentModel.DefaultBindingProperty('corner_bl')] +[Int32] +$CornerBl, +# Set the border_thickness of OBSCutRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSCutRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] [String] -$SEAWATERCOLOR, -# Set the CAMERA_SPEED of OBSSeascapeShader -[Alias('CAMERA_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] +$BorderColor, +# Set the border_alpha_start of OBSCutRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] [Single] -$CAMERASPEED, -# Set the CAMERA_TURN_SPEED of OBSSeascapeShader -[Alias('CAMERA_TURN_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] +$BorderAlphaStart, +# Set the border_alpha_end of OBSCutRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] [Single] -$CAMERATURNSPEED, +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSCutRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37164,281 +27222,185 @@ $UseShaderTime process { -$shaderName = 'seascape' -$ShaderNoun = 'OBSSeascapeShader' +$shaderName = 'cut_rect_per_corner' +$ShaderNoun = 'OBSCutRectPerCornerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * "Seascape" by Alexander Alekseev aka TDM - 2014 - * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - * Contact: tdmaav@gmail.com - * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro - */ - -#define NUM_STEPS 8 -#define PI 3.141592 -#define EPSILON 0.001 -uniform bool AA< - string label = "Smooth (more resources)"; -> = false; - -#ifndef OPENGL -#define mat2 float2x2 -#define mat3 float3x3 -#define fract frac -#define mix lerp -#endif - -// sea -#define ITER_GEOMETRY 3 -#define ITER_FRAGMENT 5 -uniform float SEA_HEIGHT< - string label = "Sea Height"; +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_tl< + string label = "Corner top left"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.5; - float step = 0.001; -> = 0.6; -uniform float SEA_CHOPPY< - string label = "Sea Choppy"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_tr< + string label = "Corner top right"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 4.0; -uniform float SEA_SPEED< - string label = "Sea Speed"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_br< + string label = "Corner bottom right"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.8; -uniform float SEA_FREQ< - string label = "Sea Frequency"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_bl< + string label = "Corner bottom left"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.16; -uniform float4 SEA_BASE< - string label = "Sea Base"; -> = {0.0,0.09,0.18,1.0}; -uniform float4 SEA_WATER_COLOR< - string label = "Sea Water"; -> = {0.48,0.54,0.36,1.0}; - -uniform float CAMERA_SPEED< - string label = "Camera Speed"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; - -uniform float CAMERA_TURN_SPEED< - string label = "Camera Turn Speed"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border aplha start"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; + float maximum = 1.0; + float step = 0.01; > = 1.0; +uniform float border_alpha_end< + string label = "Border aplha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; - -float SEA_TIME(){ - return 1.0 + elapsed_time * SEA_SPEED; -} - -// math -mat3 fromEuler(float3 ang) { - float2 a1 = float2(sin(ang.x),cos(ang.x)); - float2 a2 = float2(sin(ang.y),cos(ang.y)); - float2 a3 = float2(sin(ang.z),cos(ang.z)); - return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), - float3(-a2.y*a1.x,a1.y*a2.y,a2.x), - float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); -} - -float hash(float2 p) { - float h = dot(p,float2(127.1,311.7)); - return fract(sin(h)*43758.5453123); -} - -float noise(float2 p) { - float2 i = floor( p ); - float2 f = fract( p ); - float2 u = f*f*(3.0-2.0*f); - return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), - hash( i + float2(1.0,0.0) ), u.x), - mix( hash( i + float2(0.0,1.0) ), - hash( i + float2(1.0,1.0) ), u.x), u.y); -} - -// lighting -float diffuse(float3 n,float3 l,float p) { - return pow(dot(n,l) * 0.4 + 0.6,p); -} -float specular(float3 n,float3 l,float3 e,float s) { - float nrm = (s + 8.0) / (PI * 8.0); - return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; -} - -// sky -float3 getSkyColor(float3 e) { - e.y = (max(e.y,0.0)*0.8+0.2)*0.8; - return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; -} - -// sea -float sea_octave(float2 uv, float choppy) { - uv += noise(uv); - float2 wv = 1.0-abs(sin(uv)); - float2 swv = abs(cos(uv)); - wv = mix(wv,swv,wv); - return pow(1.0-pow(wv.x * wv.y,0.65),choppy); -} - -float map(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; - uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); - - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_GEOMETRY; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); - } - return p.y - h; -} - -float map_detailed(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_FRAGMENT; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); } - return p.y - h; -} - -float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { - float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); - fresnel = min(pow(fresnel,3.0), 0.5); - - float3 reflected = getSkyColor(reflect(eye,n)); - float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; - - float3 color = mix(refracted,reflected,fresnel); - - float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); - color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; - - float s = specular(n,l,eye,60.0); - color += float3(s,s,s); + int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; + int corner_right = corner_tr>corner_br?corner_tr:corner_br; + int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; + int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; - return color; -} - -// tracing -float3 getNormal(float3 p, float eps) { - float3 n; - n.y = map_detailed(p); - n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; - n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; - n.y = eps; - return normalize(n); -} - -float heightMapTracing(float3 ori, float3 dir, out float3 p) { - float tm = 0.0; - float tx = 1000.0; - float hx = map(ori + dir * tx); - if(hx > 0.0) { - p = ori + dir * tx; - return tx; + if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_left; } - float hm = map(ori + dir * tm); - float tmid = 0.0; - for(int i = 0; i < NUM_STEPS; i++) { - tmid = mix(tm,tx, hm/(hm-hx)); - p = ori + dir * tmid; - float hmid = map(p); - if(hmid < 0.0) { - tx = tmid; - hx = hmid; - } else { - tm = tmid; - hm = hmid; + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = closedEdgeXabs+closedEdgeYabs; + if(d>corner_radius){ + if(d-corner_radius <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + return float4(0.0,0.0,0.0,0.0); } ' } @@ -37537,30 +27499,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeasickShader { +function Get-OBSCylinderShader { -[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] +[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] param( -# Set the notes of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the amplitude of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the speed of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('speed')] +# Set the cylinder_factor of OBSCylinderShader +[Alias('cylinder_factor')] +[ComponentModel.DefaultBindingProperty('cylinder_factor')] [Single] -$Speed, -# Set the frequency of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('frequency')] +$CylinderFactor, +# Set the background_cut of OBSCylinderShader +[Alias('background_cut')] +[ComponentModel.DefaultBindingProperty('background_cut')] [Single] -$Frequency, -# Set the opacity of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, +$BackgroundCut, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37591,51 +27543,59 @@ $UseShaderTime process { -$shaderName = 'seasick' -$ShaderNoun = 'OBSSeasickShader' +$shaderName = 'cylinder' +$ShaderNoun = 'OBSCylinderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Seasick - an effect for OBS Studio -// -uniform string notes< - string widget_type = "info"; -> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; -uniform float amplitude< - string label = "amplitude"; +uniform float cylinder_factor< + string label = "Cylinder factor"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = -1.0; float maximum = 1.0; float step = 0.001; -> = 0.03; -uniform float speed< - string label = "speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float frequency< - string label = "frequency"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 6.0; -uniform float opacity< - string label = "opacity"; +> = 0.2; +uniform float background_cut< + string label = "Background cut"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; -> = 0.5; +> = 0.1; float4 mainImage(VertData v_in) : TARGET { - float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); - float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); - return image.Sample(textureSampler, coord) * opacity; -} + float2 uv = v_in.uv; + uv.x -= 0.5; + float bend = sqrt(1.0 - uv.x*uv.x*4); + uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + uv.x += 0.5; + float4 front_color = image.Sample(textureSampler, uv); + front_color.rgb *= bend/2+0.5; + if(front_color.a >= 1.0) + return front_color; + + uv = v_in.uv; + uv.x -= 0.5; + if(abs(uv.x) < background_cut) + return front_color; + uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + if(uv.x > 0){ + uv.x = 1.0 - uv.x; + }else{ + uv.x = 0 - uv.x; + } + float4 back_color = image.Sample(textureSampler, uv); + back_color.rgb *= 0.5-bend/2; + front_color.rgb *= front_color.a; + front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; + front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; + return front_color; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -37733,64 +27693,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSelectiveColorShader { +function Get-OBSDarkenShader { -[Alias('Set-OBSSelectiveColorShader','Add-OBSSelectiveColorShader')] +[Alias('Set-OBSDarkenShader','Add-OBSDarkenShader')] param( -# Set the cutoff_Red of OBSSelectiveColorShader -[Alias('cutoff_Red')] -[ComponentModel.DefaultBindingProperty('cutoff_Red')] -[Single] -$CutoffRed, -# Set the cutoff_Green of OBSSelectiveColorShader -[Alias('cutoff_Green')] -[ComponentModel.DefaultBindingProperty('cutoff_Green')] -[Single] -$CutoffGreen, -# Set the cutoff_Blue of OBSSelectiveColorShader -[Alias('cutoff_Blue')] -[ComponentModel.DefaultBindingProperty('cutoff_Blue')] -[Single] -$CutoffBlue, -# Set the cutoff_Yellow of OBSSelectiveColorShader -[Alias('cutoff_Yellow')] -[ComponentModel.DefaultBindingProperty('cutoff_Yellow')] +# Set the Opacity_Percentage of OBSDarkenShader +[Alias('Opacity_Percentage')] +[ComponentModel.DefaultBindingProperty('Opacity_Percentage')] [Single] -$CutoffYellow, -# Set the acceptance_Amplifier of OBSSelectiveColorShader -[Alias('acceptance_Amplifier')] -[ComponentModel.DefaultBindingProperty('acceptance_Amplifier')] +$OpacityPercentage, +# Set the Fill_Percentage of OBSDarkenShader +[Alias('Fill_Percentage')] +[ComponentModel.DefaultBindingProperty('Fill_Percentage')] [Single] -$AcceptanceAmplifier, -# Set the show_Red of OBSSelectiveColorShader -[Alias('show_Red')] -[ComponentModel.DefaultBindingProperty('show_Red')] -[Management.Automation.SwitchParameter] -$ShowRed, -# Set the show_Green of OBSSelectiveColorShader -[Alias('show_Green')] -[ComponentModel.DefaultBindingProperty('show_Green')] -[Management.Automation.SwitchParameter] -$ShowGreen, -# Set the show_Blue of OBSSelectiveColorShader -[Alias('show_Blue')] -[ComponentModel.DefaultBindingProperty('show_Blue')] -[Management.Automation.SwitchParameter] -$ShowBlue, -# Set the show_Yellow of OBSSelectiveColorShader -[Alias('show_Yellow')] -[ComponentModel.DefaultBindingProperty('show_Yellow')] -[Management.Automation.SwitchParameter] -$ShowYellow, -# Set the notes of OBSSelectiveColorShader -[ComponentModel.DefaultBindingProperty('notes')] +$FillPercentage, +# Set the Notes of OBSDarkenShader +[ComponentModel.DefaultBindingProperty('Notes')] [String] $Notes, -# Set the background_type of OBSSelectiveColorShader -[Alias('background_type')] -[ComponentModel.DefaultBindingProperty('background_type')] -[Int32] -$BackgroundType, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37821,126 +27741,22 @@ $UseShaderTime process { -$shaderName = 'selective_color' -$ShaderNoun = 'OBSSelectiveColorShader' +$shaderName = 'darken' +$ShaderNoun = 'OBSDarkenShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Selective Color shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//updated 4/13/2020: take into account the opacity/alpha of input image -thanks Skeletonbow for suggestion -//Converted to OpenGL by Q-mii February 25, 2020 -uniform float cutoff_Red< - string label = "cutoff Red"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.40; -uniform float cutoff_Green< - string label = "cutoff Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.025; -uniform float cutoff_Blue< - string label = "cutoff Blue"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float cutoff_Yellow< - string label = "cutoff Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float acceptance_Amplifier< - string label = "acceptance Amplifier"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 5.0; - -uniform bool show_Red = true; -uniform bool show_Green = true; -uniform bool show_Blue = true; -uniform bool show_Yellow = true; -uniform string notes< - string widget_type = "info"; -> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; -uniform int background_type< - string label = "background type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Grey"; - int option_1_value = 1; - string option_1_label = "Luma"; - int option_2_value = 2; - string option_2_label = "White"; - int option_3_value = 3; - string option_3_label = "Black"; - int option_4_value = 4; - string option_4_label = "Transparent"; - int option_5_value = 5; - string option_5_label = "Background Color"; -> = 0; +uniform float Opacity_Percentage = 100.0; +uniform float Fill_Percentage = 100.0; +uniform string Notes = "Simulates a photo editing darken layer blending mode. Fill percentage is the interior alpha and Opacity is the layer alpha."; float4 mainImage(VertData v_in) : TARGET { - const float PI = 3.1415926535897932384626433832795;//acos(-1); - const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); - float4 color = image.Sample(textureSampler, v_in.uv); - - float luminance = dot(coefLuma, color.rgb); - float4 gray = float4(luminance, luminance, luminance, 1.0); - - if (background_type == 0) - { - luminance = (color.r + color.g + color.b) * 0.3333; - gray = float4(luminance,luminance,luminance, 1.0); - } - //if (background_type == 1) - //{ - // gray = float4(luminance,luminance,luminance, 1.0); - //} - if (background_type == 2) - gray = float4(1.0,1.0,1.0,1.0); - if (background_type == 3) - gray = float4(0.0,0.0,0.0,1.0); - if (background_type == 4) - gray.a = 0.01; - if (background_type == 5) - gray = color; - - float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); - float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); - float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); - - float rgLuminance = (color.r*1.4 + color.g*0.6)/2; - float rgDiff = abs(color.r-color.g)*1.4; - - float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; - - float4 accept; - accept.r = float(show_Red) * (redness - cutoff_Red); - accept.g = float(show_Green) * (greenness - cutoff_Green); - accept.b = float(show_Blue) * (blueness - cutoff_Blue); - accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); - - float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); - float modAcceptance = min (acceptance * acceptance_Amplifier, 1); - - float4 result = color; - if (result.a > 0) { - result = modAcceptance * color + (1.0 - modAcceptance) * gray; - //result.a *= gray.a; - } + float4 other = float4(1.0, 1.0, 1.0, 1.0); + float4 base = image.Sample(textureSampler, v_in.uv); + float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); + float4 gray = float4(luminance,luminance,luminance, 1.0); - return result; + return min(base,other); } ' @@ -38040,85 +27856,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShakeShader { +function Get-OBSDeadPixelFixerShader { -[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] +[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] param( -# Set the ViewProj of OBSShakeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSShakeShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSShakeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSShakeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSShakeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSShakeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSShakeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSShakeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the local_time of OBSShakeShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the random_scale of OBSShakeShader -[Alias('random_scale')] -[ComponentModel.DefaultBindingProperty('random_scale')] -[Single] -$RandomScale, -# Set the worble of OBSShakeShader -[ComponentModel.DefaultBindingProperty('worble')] -[Management.Automation.SwitchParameter] -$Worble, -# Set the speed of OBSShakeShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSShakeShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSShakeShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] -[Single] -$MaxGrowthPixels, -# Set the randomize_movement of OBSShakeShader -[Alias('randomize_movement')] -[ComponentModel.DefaultBindingProperty('randomize_movement')] -[Management.Automation.SwitchParameter] -$RandomizeMovement, -# Set the notes of OBSShakeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +# Set the Dead_Pixel_X of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_X')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] +[Int32] +$DeadPixelX, +# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_Y')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] +[Int32] +$DeadPixelY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38149,147 +27900,64 @@ $UseShaderTime process { -$shaderName = 'shake' -$ShaderNoun = 'OBSShakeShader' +$shaderName = 'dead-pixel-fixer' +$ShaderNoun = 'OBSDeadPixelFixerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -// Added some randomization based upon random_scale input -// updated for latest version of obs-shaderfilter - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform float local_time; - +// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixel located at the user configured offset will have its color overridden by taking the average of the +// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. +// +// Changelog: +// 0.01 - Initial release -uniform float random_scale< - string label = "random scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.25; -uniform bool worble = false; -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; +uniform int Dead_Pixel_X< + string label = "Dead Pixel X"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = -2.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; +uniform int Dead_Pixel_Y< + string label = "Dead Pixel Y"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform bool randomize_movement = false; - -uniform string notes< - string widget_type = "info"; -> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -//noise values in range if 0.0 to 1.0 + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; -float noise3D(float x, float y, float z) { - float ptr = 0.0f; - return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); +float3 blur_dead_pixel(in float2 pos) +{ + float3 color; + color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; + return color * 0.25; } -VertData mainTransform(VertData v_in) +float4 mainImage( VertData v_in ) : TARGET { - VertData vert_out; + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; + float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); + float4 obstex = image.Sample(textureSampler, uv); + float3 color; - float3 pos = v_in.pos.xyz; - float t; - float s; - float noise; - if (randomize_movement) - { - t = (rand_f * 2) - 1.0f; - s = (1 - rand_f * 2) - 1.0f;; - noise = clamp( rand_f * random_scale,-0.99, 0.99); - } - else - { - t = (1 + sin(elapsed_time * speed)) / 2; - s = (1 + cos(elapsed_time * speed)) / 2; - noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); + if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { + color = blur_dead_pixel(pos); + } else { + color.rgb = obstex.rgb; } - - float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); - float3 min_pos; - float3 max_pos; - if (worble) - { - min_pos = pos + direction_from_center * min_growth_pixels * 0.5; - max_pos = pos + direction_from_center * max_growth_pixels * 0.5; - } - else - { - min_pos = pos + direction_from_center * 0.5; - max_pos = min_pos; - } - - float3 current_pos = min_pos * (1 - t) + max_pos * t; - //current_pos.x = v_in.pos.x + (t * min_pos.x); - current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); - //current_pos.y = v_in.pos.y + (s * min_pos.y); - //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; - - float2 offset = uv_offset; - offset.x = uv_offset.x * (1 - t + noise); - offset.y = uv_offset.y * (1 - s + noise); - - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - - //float2 scale = uv_scale; - //scale += dot(pos - current_pos, 1); - - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} - -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} - -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} + return float4( color, obstex.a); +} ' } @@ -38388,75 +28056,114 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShineShader { +function Get-OBSDensitySatHueShader { -[Alias('Set-OBSShineShader','Add-OBSShineShader')] +[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] param( -# Set the l_tex of OBSShineShader -[Alias('l_tex')] -[ComponentModel.DefaultBindingProperty('l_tex')] -[String] -$LTex, -# Set the shine_color of OBSShineShader -[Alias('shine_color')] -[ComponentModel.DefaultBindingProperty('shine_color')] -[String] -$ShineColor, -# Set the speed_percent of OBSShineShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the gradient_percent of OBSShineShader -[Alias('gradient_percent')] -[ComponentModel.DefaultBindingProperty('gradient_percent')] -[Int32] -$GradientPercent, -# Set the delay_percent of OBSShineShader -[Alias('delay_percent')] -[ComponentModel.DefaultBindingProperty('delay_percent')] -[Int32] -$DelayPercent, -# Set the Apply_To_Alpha_Layer of OBSShineShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the ease of OBSShineShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the hide of OBSShineShader -[ComponentModel.DefaultBindingProperty('hide')] -[Management.Automation.SwitchParameter] -$Hide, -# Set the reverse of OBSShineShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the One_Direction of OBSShineShader -[Alias('One_Direction')] -[ComponentModel.DefaultBindingProperty('One_Direction')] -[Management.Automation.SwitchParameter] -$OneDirection, -# Set the glitch of OBSShineShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSShineShader +# Set the notes of OBSDensitySatHueShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, -# Set the start_adjust of OBSShineShader -[Alias('start_adjust')] -[ComponentModel.DefaultBindingProperty('start_adjust')] +# Set the density_r of OBSDensitySatHueShader +[Alias('density_r')] +[ComponentModel.DefaultBindingProperty('density_r')] [Single] -$StartAdjust, -# Set the stop_adjust of OBSShineShader -[Alias('stop_adjust')] -[ComponentModel.DefaultBindingProperty('stop_adjust')] +$DensityR, +# Set the saturation_r of OBSDensitySatHueShader +[Alias('saturation_r')] +[ComponentModel.DefaultBindingProperty('saturation_r')] [Single] -$StopAdjust, +$SaturationR, +# Set the hueShift_r of OBSDensitySatHueShader +[Alias('hueShift_r')] +[ComponentModel.DefaultBindingProperty('hueShift_r')] +[Single] +$HueShiftR, +# Set the density_y of OBSDensitySatHueShader +[Alias('density_y')] +[ComponentModel.DefaultBindingProperty('density_y')] +[Single] +$DensityY, +# Set the saturation_y of OBSDensitySatHueShader +[Alias('saturation_y')] +[ComponentModel.DefaultBindingProperty('saturation_y')] +[Single] +$SaturationY, +# Set the hueShift_y of OBSDensitySatHueShader +[Alias('hueShift_y')] +[ComponentModel.DefaultBindingProperty('hueShift_y')] +[Single] +$HueShiftY, +# Set the density_g of OBSDensitySatHueShader +[Alias('density_g')] +[ComponentModel.DefaultBindingProperty('density_g')] +[Single] +$DensityG, +# Set the saturation_g of OBSDensitySatHueShader +[Alias('saturation_g')] +[ComponentModel.DefaultBindingProperty('saturation_g')] +[Single] +$SaturationG, +# Set the hueShift_g of OBSDensitySatHueShader +[Alias('hueShift_g')] +[ComponentModel.DefaultBindingProperty('hueShift_g')] +[Single] +$HueShiftG, +# Set the density_c of OBSDensitySatHueShader +[Alias('density_c')] +[ComponentModel.DefaultBindingProperty('density_c')] +[Single] +$DensityC, +# Set the saturation_c of OBSDensitySatHueShader +[Alias('saturation_c')] +[ComponentModel.DefaultBindingProperty('saturation_c')] +[Single] +$SaturationC, +# Set the hueShift_c of OBSDensitySatHueShader +[Alias('hueShift_c')] +[ComponentModel.DefaultBindingProperty('hueShift_c')] +[Single] +$HueShiftC, +# Set the density_b of OBSDensitySatHueShader +[Alias('density_b')] +[ComponentModel.DefaultBindingProperty('density_b')] +[Single] +$DensityB, +# Set the saturation_b of OBSDensitySatHueShader +[Alias('saturation_b')] +[ComponentModel.DefaultBindingProperty('saturation_b')] +[Single] +$SaturationB, +# Set the hueShift_b of OBSDensitySatHueShader +[Alias('hueShift_b')] +[ComponentModel.DefaultBindingProperty('hueShift_b')] +[Single] +$HueShiftB, +# Set the density_m of OBSDensitySatHueShader +[Alias('density_m')] +[ComponentModel.DefaultBindingProperty('density_m')] +[Single] +$DensityM, +# Set the saturation_m of OBSDensitySatHueShader +[Alias('saturation_m')] +[ComponentModel.DefaultBindingProperty('saturation_m')] +[Single] +$SaturationM, +# Set the hueShift_m of OBSDensitySatHueShader +[Alias('hueShift_m')] +[ComponentModel.DefaultBindingProperty('hueShift_m')] +[Single] +$HueShiftM, +# Set the global_density of OBSDensitySatHueShader +[Alias('global_density')] +[ComponentModel.DefaultBindingProperty('global_density')] +[Single] +$GlobalDensity, +# Set the global_saturation of OBSDensitySatHueShader +[Alias('global_saturation')] +[ComponentModel.DefaultBindingProperty('global_saturation')] +[Single] +$GlobalSaturation, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38487,216 +28194,294 @@ $UseShaderTime process { -$shaderName = 'shine' -$ShaderNoun = 'OBSShineShader' +$shaderName = 'density_sat_hue' +$ShaderNoun = 'OBSDensitySatHueShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -// use color to control shine amount, use transition wipes or make your own alpha texture -// slerp not currently used, for circular effects -//Converted to OpenGL by Exeldro February 14, 2022 -uniform texture2d l_tex; -uniform float4 shine_color ; -uniform int speed_percent< - string label = "speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 25; -uniform int gradient_percent< - string label = "gradient percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; -uniform int delay_percent< - string label = "delay percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool ease = false; -uniform bool hide = false; -uniform bool reverse = false; -uniform bool One_Direction = true; -uniform bool glitch = false; +// Density-Saturation-Hue Shader for OBS Shaderfilter +// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 + uniform string notes< string widget_type = "info"; -> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; +> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; -uniform float start_adjust; -uniform float stop_adjust; +uniform float density_r < + string label = "Red Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; -} +uniform float saturation_r < + string label = "Red Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; -} +uniform float hueShift_r < + string label = "Red Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float4 convert_pmalpha(float4 c) -{ - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} +uniform float density_y < + string label = "Yellow Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float4 slerp(float4 start, float4 end, float percent) -{ - // Dot product - the cosine of the angle between 2 vectors. - float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; - // Clamp it to be in the range of Acos() - // This may be unnecessary, but floating point - // precision can be a fickle mistress. - dotf = clamp(dotf, -1.0f, 1.0f); - // Acos(dot) returns the angle between start and end, - // And multiplying that by percent returns the angle between - // start and the final result. - float theta = acos(dotf)*percent; - float4 RelativeVec = normalize(end - start * dotf); - - // Orthonormal basis - // The final result. - return ((start*cos(theta)) + (RelativeVec*sin(theta))); -} +uniform float saturation_y < + string label = "Yellow Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float4 mainImage(VertData v_in) : TARGET -{ - // convert input for vector math - float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); - float speed = speed_percent * 0.01; - float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); - float delay = clamp(delay_percent * 0.01, 0.0, 1.0); - +uniform float hueShift_y < + string label = "Yellow Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // circular easing variable - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = abs(sin(elapsed_time * speed)); +uniform float density_g < + string label = "Green Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // if time is greater than direction, we are going up! - direction = t - direction; +uniform float saturation_g < + string label = "Green Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // split into segments with frac or mod. - // delay is the gap between starting and ending of the sine wave, use speed to compensate - t = (frac(t) - delay) * (1 / (1 - delay)); - t = 1 + max(t,0.0); +uniform float hueShift_g < + string label = "Green Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - float s = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration +uniform float density_c < + string label = "Cyan Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); +uniform float saturation_c < + string label = "Cyan Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - //if Unidirectional disable on return - if (One_Direction && (direction < 0.0)) - { - s = 0; - } - else - { - s = Styler(t, 0, c, d, ease); - } +uniform float hueShift_c < + string label = "Cyan Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // combine luma texture and user defined shine color - float luma = l_tex.Sample(textureSampler, v_in.uv).x; +uniform float density_b < + string label = "Blue Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // - adjust for min and max - if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) - { +uniform float saturation_b < + string label = "Blue Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if (reverse) - { - luma = 1.0 - luma; - } - - // user color with luma - float4 output_color = float4(shine_color.rgb, luma); +uniform float hueShift_b < + string label = "Blue Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); +uniform float density_m < + string label = "Magenta Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // use luma texture to add alpha and shine +uniform float saturation_m < + string label = "Magenta Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // if behind glow, consider trailing gradient shine then show underlying image - if (luma <= time - softness) - { - float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); - if (Apply_To_Alpha_Layer) - alpha_behind *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_behind); - } +uniform float hueShift_m < + string label = "Magenta Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // if in front of glow, consider if the underlying image is hidden - if (luma >= time) - { - // if hide, make the transition better - if (hide) - { - return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); - } - else - { - return rgba; - } - } +uniform float global_density < + string label = "Global Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // else show the glow area, with luminance - float alpha_ = (time - luma) / softness; - if (Apply_To_Alpha_Layer) - alpha_ *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_); - } - else - { - return rgba; - } -} +uniform float global_saturation < + string label = "Global Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | +// Tetrahedral interpolation based on the ordering of the input color channels +float3 applyAdjustments(float3 p) { + // Pre-calculate the flipped density values for each channel: + float d_r = -(density_r + global_density); + float d_y = -(density_y + global_density); + float d_g = -(density_g + global_density); + float d_c = -(density_c + global_density); + float d_b = -(density_b + global_density); + float d_m = -(density_m + global_density); + + // Compute the color vectors for each hue region using the flipped densities: + float3 red = float3( + 1.0 + d_r, + d_r - (saturation_r + global_saturation), + d_r + hueShift_r - (saturation_r + global_saturation) + ); + + float3 yellow = float3( + 1.0 + hueShift_y + d_y, + 1.0 + d_y, + d_y - (saturation_y + global_saturation) + ); + + float3 green = float3( + d_g - (saturation_g + global_saturation), + 1.0 + d_g, + d_g + hueShift_g - (saturation_g + global_saturation) + ); + + float3 cyan = float3( + d_c - (saturation_c + global_saturation), + 1.0 + hueShift_c + d_c, + 1.0 + d_c + ); + + float3 blue = float3( + d_b + hueShift_b - (saturation_b + global_saturation), + d_b - (saturation_b + global_saturation), + 1.0 + d_b + ); + + float3 magenta = float3( + 1.0 + d_m, + d_m - (saturation_m + global_saturation), + 1.0 + hueShift_m + d_m + ); + + // Define the black and white endpoints: + float3 blk = float3(0.0, 0.0, 0.0); + float3 wht = float3(1.0, 1.0, 1.0); + + float3 rgb; + if (p.r > p.g) { + if (p.g > p.b) { + // p.r >= p.g >= p.b + rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); + } else if (p.r > p.b) { + // p.r >= p.b > p.g + rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); + } else { + // p.b >= p.r > p.g + rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; + } + } else { + if (p.b > p.g) { + // p.b >= p.g >= p.r + rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; + } else if (p.b > p.r) { + // p.g >= p.r and p.b > p.r + rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); + } else { + // p.g >= p.b >= p.r + rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); + } + } + return clamp(rgb, 0.0, 1.0); +} + +float4 mainImage(VertData v_in) : TARGET { + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float3 adjustedColor = applyAdjustments(inputColor.rgb); + return float4(adjustedColor, inputColor.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | Where-Object FilterName -Match $FilterNamePattern | Remove-OBSSourceFilter } @@ -38761,49 +28546,35 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimpleGradientShader { +function Get-OBSDiffuseTransitionShader { -[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] +[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] param( -# Set the speed_percentage of OBSSimpleGradientShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSSimpleGradientShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Lens_Flair of OBSSimpleGradientShader -[Alias('Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Lens_Flair')] -[Management.Automation.SwitchParameter] -$LensFlair, -# Set the Animate_Lens_Flair of OBSSimpleGradientShader -[Alias('Animate_Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] -[Management.Automation.SwitchParameter] -$AnimateLensFlair, -# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSSimpleGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSSimpleGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +# Set the image_a of OBSDiffuseTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] [String] -$ColorToReplace, -# Set the notes of OBSSimpleGradientShader -[ComponentModel.DefaultBindingProperty('notes')] +$ImageA, +# Set the image_b of OBSDiffuseTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] [String] -$Notes, +$ImageB, +# Set the transition_time of OBSDiffuseTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSDiffuseTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the num_samples of OBSDiffuseTransitionShader +[Alias('num_samples')] +[ComponentModel.DefaultBindingProperty('num_samples')] +[Int32] +$NumSamples, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38834,125 +28605,93 @@ $UseShaderTime process { -$shaderName = 'simple_gradient' -$ShaderNoun = 'OBSSimpleGradientShader' +$shaderName = 'diffuse_transition' +$ShaderNoun = 'OBSDiffuseTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter +//based on https://www.shadertoy.com/view/4cK3z1 +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; -//lots of room to play here -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform int speed_percentage< - string label = "speed percentage"; - string widget_type = "slider"; - int minimum = -500; - int maximum = 500; - int step = 1; -> = 240; // -uniform int alpha_percentage< - string label = "aplha percentage"; +// Diffuse (move pixels) between two 2D images +// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) +// Note: the approach in this demo is different - rather than randomising paths we use means + +// increase for greater precision - this is O(n^2) :( +uniform int num_samples< + string label = "Number of samples (10)"; string widget_type = "slider"; - int minimum = 0; + int minimum = 2; int maximum = 100; int step = 1; -> = 90; -uniform bool Lens_Flair = false; -uniform bool Animate_Lens_Flair = false; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string notes< - string widget_type = "info"; -> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; +> = 10; float4 mainImage(VertData v_in) : TARGET { + float2 uv = v_in.uv; - float4 background_color = image.Sample(textureSampler, v_in.uv); - int no_colors = 4; - float3 colors[4]; - colors[0] = float3(1.0,0.0,0.0); - colors[1] = float3(0.0,1.0,0.0); - colors[2] = float3(0.0,0.0,1.0); - colors[3] = float3(1.0,1.0,1.0); - float alpha = float(alpha_percentage) * 0.01; - float speed = float(speed_percentage) * 0.01; + if (transition_time < 0.00001) { + return image_a.Sample(textureSampler, uv); + } + + // we need to normalise the distributions so just sum the samples for a division later + // note: could calculate this once per image in a buffer or something + float3 from_total = float3(0.0,0.0,0.0); + float3 to_total = float3(0.0,0.0,0.0); - float mx = max(uv_size.x , uv_size.y); - //float2 uv = v_in.uv / mx; - float3 rgb = background_color.rgb; + for (int i=0; i= 0) - { - end_color = colors[color_index - 1]; - } - else - { - end_color = colors[no_colors - 1]; - } + float2 from_pos = lerp(from_start, from_end, sample_pos); + float2 to_pos = lerp(to_start, to_end, sample_pos); - rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); -*/ - float4 rgba; - if (Apply_To_Alpha_Layer == false) - { - rgba = lerp(background_color,float4(rgb, 1.0),alpha); - } - else - { - rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); - } - if (Apply_To_Specific_Color) - { - float4 original_color = background_color; - background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; - rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); + sum += image_a.Sample(textureSampler, from_pos).rgb * (image_b.Sample(textureSampler, to_pos).rgb / to_total); + } } - return rgba; -} + + //the two distributions may have a different sum so scale to blend between the two + float3 target_total = lerp(from_total, to_total, transition_time); + float3 total_multiplier = target_total / from_total; + + sum *= total_multiplier; + if (convert_linear) + sum = srgb_nonlinear_to_linear(sum); + return float4(sum,1.0); +} ' } @@ -39051,52 +28790,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimplexNoiseShader { +function Get-OBSDigitalRainShader { -[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] +[Alias('Set-OBSDigitalRainShader','Add-OBSDigitalRainShader')] param( -# Set the Snap_Percent of OBSSimplexNoiseShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] -[Single] -$SnapPercent, -# Set the Speed_Percent of OBSSimplexNoiseShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Single] -$SpeedPercent, -# Set the Resolution of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Resolution')] -[Single] -$Resolution, -# Set the Fractal of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Fractal')] -[Management.Automation.SwitchParameter] -$Fractal, -# Set the Use_Alpha_Layer of OBSSimplexNoiseShader -[Alias('Use_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$UseAlphaLayer, -# Set the Fore_Color of OBSSimplexNoiseShader -[Alias('Fore_Color')] -[ComponentModel.DefaultBindingProperty('Fore_Color')] +# Set the font of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('font')] [String] -$ForeColor, -# Set the Back_Color of OBSSimplexNoiseShader -[Alias('Back_Color')] -[ComponentModel.DefaultBindingProperty('Back_Color')] +$Font, +# Set the noise of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('noise')] [String] -$BackColor, -# Set the Alpha_Percent of OBSSimplexNoiseShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Notes of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Notes')] +$Noise, +# Set the base_color of OBSDigitalRainShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] [String] -$Notes, +$BaseColor, +# Set the rain_speed of OBSDigitalRainShader +[Alias('rain_speed')] +[ComponentModel.DefaultBindingProperty('rain_speed')] +[Single] +$RainSpeed, +# Set the char_speed of OBSDigitalRainShader +[Alias('char_speed')] +[ComponentModel.DefaultBindingProperty('char_speed')] +[Single] +$CharSpeed, +# Set the glow_contrast of OBSDigitalRainShader +[Alias('glow_contrast')] +[ComponentModel.DefaultBindingProperty('glow_contrast')] +[Single] +$GlowContrast, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39127,204 +28852,80 @@ $UseShaderTime process { -$shaderName = 'simplex_noise' -$ShaderNoun = 'OBSSimplexNoiseShader' +$shaderName = 'digital-rain' +$ShaderNoun = 'OBSDigitalRainShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -//based upon: https://www.shadertoy.com/view/XsX3zB +// based on https://www.shadertoy.com/view/ldccW4 by WillKirkby + #ifndef OPENGL #define fract frac +#define mix lerp #endif -uniform float Snap_Percent< - string label = "Snap Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 7.5; -uniform float Speed_Percent< - string label = "Speed Percent"; +uniform texture2d font = "font.png"; +uniform texture2d noise = "noise.png"; +uniform float4 base_color = {.1, 1.0, .35, 1.0}; +uniform float rain_speed< + string label = "Rain Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 2.5; -uniform float Resolution< - string label = "Resolution"; + float minimum = 0.001; + float maximum = 2.0; + float step = .001; +> = 1.0; + +uniform float char_speed< + string label = "Character Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 16.0; -uniform bool Fractal = false; -uniform bool Use_Alpha_Layer = false; -uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; -uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; -uniform float Alpha_Percent< - string label = "Alpha Percent"; + float maximum = 2.0; + float step = .001; +> = 1.0; + +uniform float glow_contrast< + string label = "Glow contrast"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform string Notes< - string widget_type = "info"; -> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; + float maximum = 2.5; + float step = .001; +> = 1.0; -float dot(float3 a, float3 b){ - return a.r*b.r+a.g*b.g+a.b*b.b; + +float mod(float x, float y) +{ + return x - y * floor(x/y); } -float dot4(float4 a, float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x/y); } -float snap(float x, float snap) + +float text(float2 fragCoord) { - return snap * round(x / max(0.01,snap)); + float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; + float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; + uv = uv*.8+.1; // scale the letters up a bit + block += elapsed_time*.002*char_speed; + uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters + uv *= .0625; // bring back into 0-1 range + return font.Sample(textureSampler, uv).r; } -float3 random3(float3 co) +float3 rain(float2 fragCoord) { - float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = fract(512.0 * j); - j *= .125; - result.x = fract(512.0 * j); - j *= .125; - result.y = fract(512.0 * j); - return result - 0.5; -} - -/* 3d simplex noise */ -float simplex3d(float3 p) { - /* 1. find current tetrahedron T and it''s four vertices */ - /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ - /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ - - /* skew constants for 3d simplex functions */ - float F3 = 0.3333333; - float G3 = 0.1666667; - - /* calculate s and x */ - float3 s = floor(p + dot(p, float3(F3,F3,F3))); - float3 x = p - s + dot(s, float3(G3,G3,G3)); - - /* calculate i1 and i2 */ - float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); - float3 i1 = e * (1.0 - e.zxy); - float3 i2 = 1.0 - e.zxy * (1.0 - e); - - /* x1, x2, x3 */ - float3 x1 = x - i1 + G3; - float3 x2 = x - i2 + 2.0 * G3; - float3 x3 = x - 1.0 + 3.0 * G3; - - /* 2. find four surflets and store them in d */ - float4 w, d; - - /* calculate surflet weights */ - w.x = dot(x, x); - w.y = dot(x1, x1); - w.z = dot(x2, x2); - w.w = dot(x3, x3); - - /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ - w = max(0.61 - w, 0.0); - - /* calculate surflet components */ - d.x = dot(random3(s), x); - d.y = dot(random3(s + i1), x1); - d.z = dot(random3(s + i2), x2); - d.w = dot(random3(s + 1.0), x3); - - /* multiply d by w^4 */ - w *= w; - w *= w; - d *= w; - - /* 3. return the sum of the four surflets */ - return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); -} - - -/* directional artifacts can be reduced by rotating each octave */ -float simplex3d_fractal(float3 m3) { - /* const matrices for 3d rotation */ -#ifdef OPENGL - float3x3 rot1 = float3x3( - float3(-0.37, 0.36, 0.85), - float3(-0.14, -0.93, 0.34), - float3(0.92, 0.01, 0.4 )); - float3x3 rot2 = float3x3( - float3(-0.55, -0.39, 0.74), - float3(0.33, -0.91, -0.24), - float3(0.77, 0.12, 0.63 )); - float3x3 rot3 = float3x3( - float3(-0.71, 0.52, -0.47), - float3(-0.08, -0.72, -0.68), - float3(-0.7, -0.45, 0.56 )); - - float3 m = float3(m3.x, m3.y, m3.z); -#else - float3x3 rot1 = { - -0.37, 0.36, 0.85, - -0.14, -0.93, 0.34, - 0.92, 0.01, 0.4 }; - float3x3 rot2 = { - -0.55, -0.39, 0.74, - 0.33, -0.91, -0.24, - 0.77, 0.12, 0.63 }; - float3x3 rot3 = { - -0.71, 0.52, -0.47, - -0.08, -0.72, -0.68, - -0.7, -0.45, 0.56 }; - - float3 m = {m3.x, m3.y, m3.z}; -#endif - - return 0.5333333* simplex3d(mul(m, rot1)) - + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) - + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) - + 0.0666667 * simplex3d(8.0 * m); + fragCoord.x -= mod(fragCoord.x, 16.); + float offset=sin(fragCoord.x*15.); + float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; + + float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); + return base_color.rgb / pow(y*20.0, glow_contrast); } float4 mainImage(VertData v_in) : TARGET { - float time = snap(elapsed_time, Snap_Percent * .01); - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 p = v_in.uv.xy + float2( 0, -0.5); - float3 p3 = float3(p, time * (Speed_Percent * 0.01)); - - float pixel_alpha = 1.0; - // apply to mainImage rgba - if (Use_Alpha_Layer) { - p3 *= rgba.rgb; - pixel_alpha = rgba.a; - } - - float value; - - if (Fractal) { - value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); - } - else { - value = simplex3d(p3 * Resolution); - } - - //soften color - value = 0.5 + (0.5 * value); - float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); - - //use intensity to apply foreground and background colors - float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); - r = lerp(Back_Color, r, saturate(intensity)); - r.a = pixel_alpha; - - return lerp(rgba, r, Alpha_Percent * 0.01); + return mix(image.Sample(textureSampler, v_in.uv),float4(rain(float2(v_in.uv.x,1.0-v_in.uv.y)*uv_size),1.0), text(v_in.uv*uv_size)); } ' } @@ -39423,22 +29024,33 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSmartDenoiseShader { +function Get-OBSDivideRotateShader { -[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] +[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] param( -# Set the uSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uSigma')] -[Single] -$USigma, -# Set the uKSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uKSigma')] -[Single] -$UKSigma, -# Set the uThreshold of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uThreshold')] -[Single] -$UThreshold, +# Set the iChannel0 of OBSDivideRotateShader +[ComponentModel.DefaultBindingProperty('iChannel0')] +[String] +$IChannel0, +# Set the speed_percentage of OBSDivideRotateShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSDivideRotateShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the notes of OBSDivideRotateShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39469,87 +29081,90 @@ $UseShaderTime process { -$shaderName = 'smart_denoise' -$ShaderNoun = 'OBSSmartDenoiseShader' +$shaderName = 'divide_rotate' +$ShaderNoun = 'OBSDivideRotateShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) -// Converted to OBS version of HLSL by Euiko on February 10, 2025 - -#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI -#define INV_PI 0.31830988618379067153776752674503 - -uniform float uSigma< - string label = "Sigma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3; // max based on the webgl sample, which is 3 - float step = 0.01; -> = 5.0; // default value based on shadertoy -uniform float uKSigma< - string label = "K-Sigma"; +// divide and rotate shader for OBS Studio shaderfilter plugin +// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) +// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform texture2d iChannel0; +uniform int speed_percentage< + string label = "Speed"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 24; // max based on the webgl sample, which is 24 - float step = 0.01; -> = 7.0; // the default value is based on the webgl sample -uniform float uThreshold< - string label = "Edge Threshold"; + int minimum = -10; + int maximum = 10; + int step = 1; +> = 5; +uniform int alpha_percentage< + string label = "Opacity Percentage"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 2; // max based on the webgl sample, which is 2 - float step = 0.01; -> = 0.190; // the default value is based on the webgl sample - -// smartDeNoise - parameters -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// float2 uv - actual fragment coord -// float2 size - window size -// float sigma > 0 - sigma Standard Deviation -// float kSigma >= 0 - sigma coefficient -// kSigma * sigma --> radius of the circular kernel -// float threshold - edge sharpening threshold -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default -float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) -{ - float radius = round(kSigma * sigma); - float radQ = radius * radius; - - float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) - float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = true; - float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) - float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) +uniform string notes< + string widget_type = "info"; +> = "add rotation and speed"; - float4 centrPx = image.Sample(textureSampler, uv); - float zBuff = 0.0; - float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); +float2 cm(float2 a, float2 b) { + return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} - float2 d; - for (d.x = -radius; d.x <= radius; d.x += 1.0) - { - float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend - d.y = -pt; - for (; d.y <= pt; d.y += 1.0) - { - float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; - float4 walkPx = image.Sample(textureSampler, uv + (d / size)); - float4 dC = walkPx - centrPx; - float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; - zBuff += deltaFactor; - aBuff += (walkPx * deltaFactor); - } - } - return aBuff / float4(zBuff, zBuff, zBuff, zBuff); +float2 iter(float2 uv, float2 rot, float scale) { + float2 gv = frac(cm(uv, rot) * scale); + float boundDist = 1. - max(abs(gv.x), abs(gv.y)); + float mask = step(.03, boundDist); + gv *= mask; + return gv; } float4 mainImage(VertData v_in) : TARGET { - return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); -} + float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); + float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); + + // Normalize coords + //float2 uv = (v_in.uv * uv_scale + uv_offset); + float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; + float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; + + // Add some time rotation and offset + float t = elapsed_time * speed; + float2 time = float2(sin(t), cos(t)); + uv += time; + + // Imaginary component has to be mirrored for natural feeling rotation + mouse.y *= -1.0; + + // Draw few layers of this to bend space + float2 rot = cm(mouse, time); + for (float i=1.0; i<=3.0; i++) { + uv = iter(uv, rot, 1.5); + } + + // Combine background with new image + float4 background_color = image.Sample(textureSampler, v_in.uv); + float4 col = iChannel0.Sample(textureSampler, uv); + + // Border + if (uv.x == 0.0 && uv.y == 0.0) { + col = float4(0,0,0,alpha); + } + + // if not appling to alpha layer, set output alpha + if (Apply_To_Alpha_Layer == false) + col.a = alpha; + //output color is combined with background image + col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); + + return col; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -39647,47 +29262,62 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecularShineShader { +function Get-OBSDoodleShader { -[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] +[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] param( -# Set the Hint of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('Hint')] +# Set the ViewProj of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$Hint, -# Set the roughness of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('roughness')] -[Single] -$Roughness, -# Set the lightStrength of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('lightStrength')] -[Single] -$LightStrength, -# Set the LightPositionX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionX')] -[Single] -$LightPositionX, -# Set the LightPositionY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionY')] +$Image, +# Set the elapsed_time of OBSDoodleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$LightPositionY, -# Set the flattenNormal of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('flattenNormal')] +$ElapsedTime, +# Set the uv_offset of OBSDoodleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSDoodleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSDoodleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSDoodleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$FlattenNormal, -# Set the stretchNormalX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalX')] +$RandF, +# Set the uv_size of OBSDoodleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the Doodle_Scale_Percent of OBSDoodleShader +[Alias('Doodle_Scale_Percent')] +[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] [Single] -$StretchNormalX, -# Set the stretchNormalY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalY')] +$DoodleScalePercent, +# Set the Snap_Percent of OBSDoodleShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] [Single] -$StretchNormalY, -# Set the Light_Color of OBSSpecularShineShader -[Alias('Light_Color')] -[ComponentModel.DefaultBindingProperty('Light_Color')] -[Single[]] -$LightColor, +$SnapPercent, +# Set the Notes of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39718,108 +29348,94 @@ $UseShaderTime process { -$shaderName = 'specular-shine' -$ShaderNoun = 'OBSSpecularShineShader' +$shaderName = 'doodle' +$ShaderNoun = 'OBSDoodleShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft - -uniform string Hint< - string widget_type = "info"; -> = "Try using a black color source with the additive blend mode!"; +// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float roughness< - string label = "Roughness"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.25; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; -uniform float lightStrength< - string label = "lightStrength"; +uniform float Doodle_Scale_Percent< + string label = "Doodle Scale Percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; - -uniform float LightPositionX< - string label = "Light Position X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float LightPositionY< - string label = "Light Position Y"; + float maximum = 100.0; + float step = 0.1; +> = 2.5; +uniform float Snap_Percent< + string label = "Snap Percent"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.1; +> = 7.5; +uniform string Notes< + string widget_type = "info"; +> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; -uniform float flattenNormal< - string label = "Flatten Normal"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -uniform float stretchNormalX< - string label = "Stretch Normal X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -uniform float stretchNormalY< - string label = "Stretch Normal Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; +float3 rand3(float3 co) +{ + float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = frac(512.0*j); + j *= .125; + result.x = frac(512.0*j); + j *= .125; + result.y = frac(512.0*j); + return result - 0.5; +} -uniform float3 Light_Color = {1,1,1}; +float snap(float x, float snap) +{ + return snap * round(x / max(0.01,snap)); +} -float Square(float a) { return a * a; } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); + float rand = snap(rand_f, Snap_Percent *.01); + float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); + vert_out.uv.xy += noise; -float CookTorrance(float3 lightDir, float3 normal, float roughness) { - float3 h = normalize(lightDir + float3(0,0,1)); - float nh2 = Square(saturate(dot(normal, h))); - float lh2 = Square(saturate(dot(lightDir, h))); - float r2 = Square(roughness); - float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); - float normalization = roughness * 4.0 + 2.0; - return r2 / (d2 * max(0.1, lh2) * normalization); + return vert_out; } -#define PI 3.14159265 - float4 mainImage(VertData v_in) : TARGET { + return image.Sample(textureSampler, v_in.uv); +} - float4 c0 = image.Sample(textureSampler, v_in.uv); - - float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); - - float2 normalUV = v_in.uv - 0.5; - normalUV.x /= stretchNormalX; - normalUV.y /= stretchNormalY; - normalUV += 0.5; - - float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); - normal.z *= -1; - - normal = lerp(normal, float3(0,0,-1), flattenNormal); - - float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; - - return float4(c0 + light,c0.a); +technique Draw +{ + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } ' @@ -39919,43 +29535,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpotlightShader { +function Get-OBSDrawingsShader { -[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] +[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] param( -# Set the Speed_Percent of OBSSpotlightShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Single] -$SpeedPercent, -# Set the Focus_Percent of OBSSpotlightShader -[Alias('Focus_Percent')] -[ComponentModel.DefaultBindingProperty('Focus_Percent')] -[Single] -$FocusPercent, -# Set the Glitch of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Spotlight_Color of OBSSpotlightShader -[Alias('Spotlight_Color')] -[ComponentModel.DefaultBindingProperty('Spotlight_Color')] -[String] -$SpotlightColor, -# Set the Horizontal_Offset of OBSSpotlightShader -[Alias('Horizontal_Offset')] -[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] -[Single] -$HorizontalOffset, -# Set the Vertical_Offset of OBSSpotlightShader -[Alias('Vertical_Offset')] -[ComponentModel.DefaultBindingProperty('Vertical_Offset')] -[Single] -$VerticalOffset, -# Set the Notes of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, +# Set the AngleNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('AngleNum')] +[Int32] +$AngleNum, +# Set the SampNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('SampNum')] +[Int32] +$SampNum, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39986,62 +29577,143 @@ $UseShaderTime process { -$shaderName = 'spotlight' -$ShaderNoun = 'OBSSpotlightShader' +$shaderName = 'drawings' +$ShaderNoun = 'OBSDrawingsShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -uniform float Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform float Focus_Percent< - string label = "Focus Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 15.0; -uniform bool Glitch; -uniform float4 Spotlight_Color; -uniform float Horizontal_Offset< - string label = "Horizontal Offset"; +//based on https://www.shadertoy.com/view/ldlcWs + +uniform int AngleNum< + string label = "Number of angles"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Vertical_Offset< - string label = "Vertical Offset"; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 3; +uniform int SampNum< + string label = "Number of samples"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.5; -uniform string Notes< - string widget_type = "info"; -> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 9; + +float4 getCol(float2 pos) +{ + // take aspect ratio into account + float2 uv=pos; + float4 c1=image.Sample(textureSampler, uv); + float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); + c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); + float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); + float4 c2=float4(.7,.7,.7,.7); + return min(lerp(c1,c2,1.8*d),.7); +} + +float4 getColHT(float2 pos) +{ + return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); +} + +float getVal(float2 pos) +{ + float4 c=getCol(pos); + return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; +} + +float2 getGrad(float2 pos, float eps) +{ + float2 d=float2(eps,0.); + return float2( + getVal(pos+d.xy)-getVal(pos-d.xy), + getVal(pos+d.yx)-getVal(pos-d.yx) + )/eps/2.; +} + + + float lum( float3 c) { + return dot(c, float3(0.3, 0.59, 0.11)); + } + + + float3 clipcolor( float3 c) { + float l = lum(c); + float n = min(min(c.r, c.g), c.b); + float x = max(max(c.r, c.g), c.b); + + if (n < 0.0) { + c.r = l + ((c.r - l) * l) / (l - n); + c.g = l + ((c.g - l) * l) / (l - n); + c.b = l + ((c.b - l) * l) / (l - n); + } + if (x > 1.25) { + c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); + c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); + c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); + } + return c; + } + + float3 setlum( float3 c, float l) { + float d = l - lum(c); + c = c + float3(d,d,d); + return clipcolor(0.85*c); + } float4 mainImage(VertData v_in) : TARGET { - float speed = Speed_Percent * 0.01; - float focus = Focus_Percent; - if (Glitch) - { - speed *= ((rand_f * 2) - 1) * 0.01; - focus *= ((rand_f * 1.1) - 0.1); - } + float2 pos = v_in.uv; + float3 col = float3(0,0,0); + float3 col2 = float3(0,0,0); + float sum=0.; + + for(int i=0;i = 0.5; // -uniform float angle< - string label = "Angle"; +// Drop Shadow shader modified by Charles Fettinger +// impose a limiter to keep from crashing the system +//Converted to OpenGL by Exeldro February 19, 2022 +uniform int shadow_offset_x< + string label = "Shadow offset x"; string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.01; -> = 270.0; // - -uniform float center_x< - string label = "Center x"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_offset_y< + string label = "Shadow offset y"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // -uniform float center_y< - string label = "Center y"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_blur_size< + string label = "Shadow blur size"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // - -uniform bool animate = false; -uniform bool inverse = false; - + int minimum = 0; + int maximum = 15; + int step = 1; +> = 3; uniform string notes< string widget_type = "info"; -> = "Distorts the screen, twisting the image in a circular motion." +> = "blur size is limited to a max of 15 to ensure GPU"; + +uniform float4 shadow_color; + +uniform bool is_alpha_premultiplied; float4 mainImage(VertData v_in) : TARGET { + int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); + int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - - v_out.uv = 1. * v_in.uv - center; + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); - center.x /= ar; - v_out.uv.x /= ar; + float sampled_shadow_alpha = 0.0; - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float percent = (radius-dist)/(radius); - percent = inverse == false ? percent : 1 - percent; - - float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; - - return image.Sample(textureSampler, v_out.uv); - } - else + for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) { - return image.Sample(textureSampler, v_in.uv ); + for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); + } } - + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); + return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); } ' @@ -40380,310 +30032,246 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTetraShader { +function Get-OBSDrunkShader { -[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] +[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] param( -# Set the redR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redR')] -[Single] -$RedR, -# Set the redG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redG')] -[Single] -$RedG, -# Set the redB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redB')] -[Single] -$RedB, -# Set the yelR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelR')] -[Single] -$YelR, -# Set the yelG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelG')] -[Single] -$YelG, -# Set the yelB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelB')] -[Single] -$YelB, -# Set the grnR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnR')] -[Single] -$GrnR, -# Set the grnG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnG')] -[Single] -$GrnG, -# Set the grnB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnB')] -[Single] -$GrnB, -# Set the cynR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynR')] -[Single] -$CynR, -# Set the cynG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynG')] -[Single] -$CynG, -# Set the cynB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynB')] -[Single] -$CynB, -# Set the bluR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluR')] -[Single] -$BluR, -# Set the bluG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluG')] -[Single] -$BluG, -# Set the bluB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluB')] -[Single] -$BluB, -# Set the magR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magR')] -[Single] -$MagR, -# Set the magG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magG')] -[Single] -$MagG, -# Set the magB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magB')] -[Single] -$MagB, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - +# Set the color_matrix of OBSDrunkShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the glow_percent of OBSDrunkShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSDrunkShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSDrunkShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed_percent of OBSDrunkShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the Apply_To_Alpha_Layer of OBSDrunkShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the glow_color of OBSDrunkShader +[Alias('glow_color')] +[ComponentModel.DefaultBindingProperty('glow_color')] +[String] +$GlowColor, +# Set the ease of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the glitch of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + process { -$shaderName = 'tetra' -$ShaderNoun = 'OBSTetraShader' +$shaderName = 'drunk' +$ShaderNoun = 'OBSDrunkShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Tetrahedral Interpolation Shader for OBS - -uniform float redR< - string label = "Red in Red"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +uniform float4x4 color_matrix; -uniform float redG< - string label = "Green in Red"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float redB< - string label = "Blue in Red"; +uniform int glow_percent< + string label = "Glow percent"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float yelR< - string label = "Red in Yellow"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float yelG< - string label = "Green in Yellow"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float yelB< - string label = "Blue in Yellow"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float grnR< - string label = "Red in Green"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform float4 glow_color; +uniform bool ease; +uniform bool glitch; +uniform string notes< + string widget_type = "info"; +> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; -uniform float grnG< - string label = "Green in Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float grnB< - string label = "Blue in Green"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +// Gaussian Blur +float Gaussian(float x, float o) { + const float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); +} -uniform float cynR< - string label = "Red in Cyan"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float cynG< - string label = "Green in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; +} -uniform float cynB< - string label = "Blue in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +float BlurStyler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; +} -uniform float bluR< - string label = "Red in Blue"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + } + return l_value; +} -uniform float bluG< - string label = "Green in Blue"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 offsets[4]; + offsets[0] = float2(-0.05, 0.066); + offsets[1] = float2(-0.05, -0.066); + offsets[2] = float2(0.05, -0.066); + offsets[3] = float2(0.05, 0.066); -uniform float bluB< - string label = "Blue in Blue"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + // convert input for vector math + float blur_amount = float(blur) /100; + float glow_amount = float(glow_percent) * 0.1; + float speed = float(pulse_speed_percent) * 0.01; + float luminance_floor = float(min_brightness) * 0.01; + float luminance_ceiling = float(max_brightness) * 0.01; -uniform float magR< - string label = "Red in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + float4 color = image.Sample(textureSampler, v_in.uv); + float4 temp_color = color; + bool glitch_on = glitch; -uniform float magG< - string label = "Green in Magenta"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + //circular easing variable + float t = 1 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration -uniform float magB< - string label = "Blue in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); + float4 glitch_color = glow_color; + for (int n = 0; n < 4; n++){ + //blur sample + b = BlurStyler(t,0,c,d,ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; -float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { - float r = RGBimage.x; - float g = RGBimage.y; - float b = RGBimage.z; + //test for rand_f color + if (glitch) { + glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); + if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) + { + glitch_on = true; + } + } - float3 wht = float3(1.0, 1.0, 1.0); + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance + ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color + glitch_on) //test for rand color + { + //glow calc + if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); + //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); - if (r > g) { - if (g > b) { - // r > g > b - return r * red + g * (yel - red) + b * (wht - yel); - } else if (r > b) { - // r > b > g - return r * red + g * (wht - mag) + b * (mag - red); - } else { - // b > r > g - return r * (mag - blu) + g * (wht - mag) + b * blu; - } - } else { - if (b > g) { - // b > g > r - return r * (wht - cyn) + g * (cyn - blu) + b * blu; - } else if (b > r) { - // g > b > r - return r * (wht - cyn) + g * grn + b * (cyn - grn); - } else { - // g > r > b - return r * (yel - grn) + g * grn + b * (wht - yel); - } - } + // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color + // max is used to simulate addition of vector texture color + temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time + max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time + * (glitch_color * (b / 2)); // glow color affected by glow over time + } + } + } + // grab lighter color + return max(color,temp_color); } -float4 mainImage(VertData v_in) : TARGET -{ - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float alpha = inputColor.a; - - float3 red = float3(redR, redG, redB); - float3 yel = float3(yelR, yelG, yelB); - float3 grn = float3(grnR, grnG, grnB); - float3 cyn = float3(cynR, cynG, cynB); - float3 blu = float3(bluR, bluG, bluB); - float3 mag = float3(magR, magG, magB); - - float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); - return float4(outputColor, alpha); -} ' } @@ -40782,14 +30370,135 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSThermalShader { +function Get-OBSDynamicMaskShader { -[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] +[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] param( -# Set the strength of OBSThermalShader -[ComponentModel.DefaultBindingProperty('strength')] +# Set the input_source of OBSDynamicMaskShader +[Alias('input_source')] +[ComponentModel.DefaultBindingProperty('input_source')] +[String] +$InputSource, +# Set the red_base_value of OBSDynamicMaskShader +[Alias('red_base_value')] +[ComponentModel.DefaultBindingProperty('red_base_value')] [Single] -$Strength, +$RedBaseValue, +# Set the red_red_input_value of OBSDynamicMaskShader +[Alias('red_red_input_value')] +[ComponentModel.DefaultBindingProperty('red_red_input_value')] +[Single] +$RedRedInputValue, +# Set the red_green_input_value of OBSDynamicMaskShader +[Alias('red_green_input_value')] +[ComponentModel.DefaultBindingProperty('red_green_input_value')] +[Single] +$RedGreenInputValue, +# Set the red_blue_input_value of OBSDynamicMaskShader +[Alias('red_blue_input_value')] +[ComponentModel.DefaultBindingProperty('red_blue_input_value')] +[Single] +$RedBlueInputValue, +# Set the red_alpha_input_value of OBSDynamicMaskShader +[Alias('red_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] +[Single] +$RedAlphaInputValue, +# Set the red_multiplier of OBSDynamicMaskShader +[Alias('red_multiplier')] +[ComponentModel.DefaultBindingProperty('red_multiplier')] +[Single] +$RedMultiplier, +# Set the green_base_value of OBSDynamicMaskShader +[Alias('green_base_value')] +[ComponentModel.DefaultBindingProperty('green_base_value')] +[Single] +$GreenBaseValue, +# Set the green_red_input_value of OBSDynamicMaskShader +[Alias('green_red_input_value')] +[ComponentModel.DefaultBindingProperty('green_red_input_value')] +[Single] +$GreenRedInputValue, +# Set the green_green_input_value of OBSDynamicMaskShader +[Alias('green_green_input_value')] +[ComponentModel.DefaultBindingProperty('green_green_input_value')] +[Single] +$GreenGreenInputValue, +# Set the green_blue_input_value of OBSDynamicMaskShader +[Alias('green_blue_input_value')] +[ComponentModel.DefaultBindingProperty('green_blue_input_value')] +[Single] +$GreenBlueInputValue, +# Set the green_alpha_input_value of OBSDynamicMaskShader +[Alias('green_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] +[Single] +$GreenAlphaInputValue, +# Set the green_multiplier of OBSDynamicMaskShader +[Alias('green_multiplier')] +[ComponentModel.DefaultBindingProperty('green_multiplier')] +[Single] +$GreenMultiplier, +# Set the blue_base_value of OBSDynamicMaskShader +[Alias('blue_base_value')] +[ComponentModel.DefaultBindingProperty('blue_base_value')] +[Single] +$BlueBaseValue, +# Set the blue_red_input_value of OBSDynamicMaskShader +[Alias('blue_red_input_value')] +[ComponentModel.DefaultBindingProperty('blue_red_input_value')] +[Single] +$BlueRedInputValue, +# Set the blue_green_input_value of OBSDynamicMaskShader +[Alias('blue_green_input_value')] +[ComponentModel.DefaultBindingProperty('blue_green_input_value')] +[Single] +$BlueGreenInputValue, +# Set the blue_blue_input_value of OBSDynamicMaskShader +[Alias('blue_blue_input_value')] +[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] +[Single] +$BlueBlueInputValue, +# Set the blue_alpha_input_value of OBSDynamicMaskShader +[Alias('blue_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] +[Single] +$BlueAlphaInputValue, +# Set the blue_multiplier of OBSDynamicMaskShader +[Alias('blue_multiplier')] +[ComponentModel.DefaultBindingProperty('blue_multiplier')] +[Single] +$BlueMultiplier, +# Set the alpha_base_value of OBSDynamicMaskShader +[Alias('alpha_base_value')] +[ComponentModel.DefaultBindingProperty('alpha_base_value')] +[Single] +$AlphaBaseValue, +# Set the alpha_red_input_value of OBSDynamicMaskShader +[Alias('alpha_red_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] +[Single] +$AlphaRedInputValue, +# Set the alpha_green_input_value of OBSDynamicMaskShader +[Alias('alpha_green_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] +[Single] +$AlphaGreenInputValue, +# Set the alpha_blue_input_value of OBSDynamicMaskShader +[Alias('alpha_blue_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] +[Single] +$AlphaBlueInputValue, +# Set the alpha_alpha_input_value of OBSDynamicMaskShader +[Alias('alpha_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] +[Single] +$AlphaAlphaInputValue, +# Set the alpha_multiplier of OBSDynamicMaskShader +[Alias('alpha_multiplier')] +[ComponentModel.DefaultBindingProperty('alpha_multiplier')] +[Single] +$AlphaMultiplier, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -40820,49 +30529,222 @@ $UseShaderTime process { -$shaderName = 'thermal' -$ShaderNoun = 'OBSThermalShader' +$shaderName = 'dynamic-mask' +$ShaderNoun = 'OBSDynamicMaskShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/mdKXzG +uniform texture2d input_source< + string label = "Input Source"; +>; -uniform float strength< - string label = "Strength"; +uniform float red_base_value< + string label = "Base Value"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.1; -> = 100.0; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float red_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -float greyScale(float3 c) { - return 0.29 * c.r + 0.60 * c.g + 0.11; -} +uniform float green_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float green_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -float3 heatMap(float greyValue) { - float3 heat; - heat.r = smoothstep(0.5, 0.8, greyValue); - if(greyValue >= 0.8333) { - heat.r *= (1.1 - greyValue) * 5.0; - } - if(greyValue > 0.6) { - heat.g = smoothstep(1.0, 0.7, greyValue); - } else { - heat.g = smoothstep(0.0, 0.7, greyValue); - } - heat.b = smoothstep(1.0, 0.0, greyValue); - if(greyValue <= 0.3333) { - heat.b *= greyValue / 0.3; - } - return heat; -} +uniform float blue_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float blue_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; + +uniform float alpha_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float alpha_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - float greyValue = greyScale(c.rgb); - float3 h = heatMap(greyValue*(strength/100.0)); - return float4(h.r, h.g, h.b, c.a); + float4 input_color = input_source.Sample(textureSampler, v_in.uv); + float4 mask; + mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; + mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; + mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; + mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; + float4 base = image.Sample(textureSampler, v_in.uv); + return base * mask; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -40960,26 +30842,63 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTvCrtSubpixelShader { +function Get-OBSEdgeDetectionShader { -[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] +[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] param( -# Set the channelWidth of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelWidth')] -[Int32] -$ChannelWidth, -# Set the channelHeight of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelHeight')] -[Int32] -$ChannelHeight, -# Set the hGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('hGap')] -[Int32] -$HGap, -# Set the vGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('vGap')] -[Int32] -$VGap, +# Set the sensitivity of OBSEdgeDetectionShader +[ComponentModel.DefaultBindingProperty('sensitivity')] +[Single] +$Sensitivity, +# Set the invert_edge of OBSEdgeDetectionShader +[Alias('invert_edge')] +[ComponentModel.DefaultBindingProperty('invert_edge')] +[Management.Automation.SwitchParameter] +$InvertEdge, +# Set the edge_color of OBSEdgeDetectionShader +[Alias('edge_color')] +[ComponentModel.DefaultBindingProperty('edge_color')] +[String] +$EdgeColor, +# Set the edge_multiply of OBSEdgeDetectionShader +[Alias('edge_multiply')] +[ComponentModel.DefaultBindingProperty('edge_multiply')] +[Management.Automation.SwitchParameter] +$EdgeMultiply, +# Set the non_edge_color of OBSEdgeDetectionShader +[Alias('non_edge_color')] +[ComponentModel.DefaultBindingProperty('non_edge_color')] +[String] +$NonEdgeColor, +# Set the non_edge_multiply of OBSEdgeDetectionShader +[Alias('non_edge_multiply')] +[ComponentModel.DefaultBindingProperty('non_edge_multiply')] +[Management.Automation.SwitchParameter] +$NonEdgeMultiply, +# Set the alpha_channel of OBSEdgeDetectionShader +[Alias('alpha_channel')] +[ComponentModel.DefaultBindingProperty('alpha_channel')] +[Management.Automation.SwitchParameter] +$AlphaChannel, +# Set the alpha_level of OBSEdgeDetectionShader +[Alias('alpha_level')] +[ComponentModel.DefaultBindingProperty('alpha_level')] +[Single] +$AlphaLevel, +# Set the alpha_invert of OBSEdgeDetectionShader +[Alias('alpha_invert')] +[ComponentModel.DefaultBindingProperty('alpha_invert')] +[Management.Automation.SwitchParameter] +$AlphaInvert, +# Set the rand_f of OBSEdgeDetectionShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the notes of OBSEdgeDetectionShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41010,80 +30929,102 @@ $UseShaderTime process { -$shaderName = 'tv-crt-subpixel' -$ShaderNoun = 'OBSTvCrtSubpixelShader' +$shaderName = 'edge_detection' +$ShaderNoun = 'OBSEdgeDetectionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro - -// width of a single color channel in pixels -uniform int channelWidth< - string label = "Channel Width"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; - -// height of color channels in pixels -uniform int channelHeight< - string label = "Channel Height"; +// Edge Detection for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform float sensitivity< + string label = "Sensitivity"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 3; - -// horizontal distance between two neighboring pixels -uniform int hGap< - string label = "Horizontal Gap"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.05; +uniform bool invert_edge; +uniform float4 edge_color = {1.0,1.0,1.0,1.0}; +uniform bool edge_multiply; +uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; +uniform bool non_edge_multiply; +uniform bool alpha_channel; +uniform float alpha_level< + string label = "Alpha level"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform bool alpha_invert; +uniform float rand_f; -// vertical distance between two neighboring pixels -uniform int vGap< - string label = "Vertical Gap"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; +uniform string notes< + string widget_type = "info"; +> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; float4 mainImage(VertData v_in) : TARGET { - float columns = float(channelWidth * 3 + hGap); - float pixelHeight = float(channelHeight + vGap); - - float2 fragCoord = v_in.uv * uv_size; - float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); - float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); - float2 sampleUv = pixel / sampleRes; - - // color of sample point - float4 col = image.Sample(textureSampler, sampleUv); - - int column = int(fragCoord.x) % (channelWidth * 3 + hGap); - - // set color based on which channel this fragment corresponds to - if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); - else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); - else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); - else col = float4(0.0, 0.0, 0.0, col.a); + float4 c = image.Sample(textureSampler, v_in.uv); + + float s = 3; + float hstep = uv_pixel_interval.x; + float vstep = uv_pixel_interval.y; + + float offsetx = (hstep * s) / 2.0; + float offsety = (vstep * s) / 2.0; + + float4 lum = float4(0.30, 0.59, 0.11, 1 ); + float samples[9]; + + int index = 0; + for(int i = 0; i < s; i++){ + for(int j = 0; j < s; j++){ + samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); + index++; + } + } + + float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; + float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; + float4 col; + + float o = ((vert * vert) + (hori * hori)); + bool isEdge = o > sensitivity; + if(invert_edge){ + isEdge = !isEdge; + } + if(isEdge) { + col = edge_color; + if(edge_multiply){ + col *= c; + } + } else { + col = non_edge_color; + if(non_edge_multiply){ + col *= c; + } + } - // offset every other column of pixels - int height = int(pixelHeight); - if (int(pixel.x) % 2 == 0) { - if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); - } else { - if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); - } + if (alpha_invert) { + lum = 1.0 - lum; + } - // Output to screen - return col; + if(alpha_channel){ + if (edge_multiply && isEdge) { + return clamp(lerp(c, col, alpha_level), 0.0, 1.0); + } + else { + // use max instead of multiply + return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); + } + } else { + // col.a = col.a * alpha_level; + return col; + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -41181,111 +31122,508 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwistShader { +function Get-OBSEmbersShader { -[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] +[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] param( -# Set the center_x_percent of OBSTwistShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSTwistShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the power of OBSTwistShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the rotation of OBSTwistShader -[ComponentModel.DefaultBindingProperty('rotation')] -[Single] -$Rotation, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] +# Set the ViewProj of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +$Image, +# Set the elapsed_time of OBSEmbersShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSEmbersShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSEmbersShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSEmbersShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSEmbersShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSEmbersShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSEmbersShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSEmbersShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSEmbersShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the Animation_Speed of OBSEmbersShader +[Alias('Animation_Speed')] +[ComponentModel.DefaultBindingProperty('Animation_Speed')] +[Single] +$AnimationSpeed, +# Set the Movement_Direction_Horizontal of OBSEmbersShader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +[Single] +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSEmbersShader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Movement_Speed_Percent of OBSEmbersShader +[Alias('Movement_Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] +[Int32] +$MovementSpeedPercent, +# Set the Layers_Count of OBSEmbersShader +[Alias('Layers_Count')] +[ComponentModel.DefaultBindingProperty('Layers_Count')] +[Int32] +$LayersCount, +# Set the lumaMin of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Alpha_Percentage of OBSEmbersShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSEmbersShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { -$shaderName = 'twist' -$ShaderNoun = 'OBSTwistShader' +$shaderName = 'embers' +$ShaderNoun = 'OBSEmbersShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percentage"; +// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated + +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +uniform string notes< + string widget_type = "info"; +> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; + +#ifndef OPENGL +#define mat2 float2x2 +#define fract frac +#define mix lerp +#endif + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +uniform float Animation_Speed < + string label = "Animation Speed"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; + float scale = 1.; +> = 1.5; + +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 1.0; +> = 5.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 1.0; +> = 10.0; + +uniform int Movement_Speed_Percent< + string label = "Movement Speed Percent"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; +> = 5; + +uniform int Layers_Count < + string label = "Layers"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; + int minimum = 1.0; + int maximum = 100.0; int step = 1; -> = 50; -uniform float power< - string label = "power"; +> = 15; +/* ps start +*/ + + +uniform float lumaMin< + string label = "Luma Min"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.3; -uniform float rotation< - string label = "rotation"; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; string widget_type = "slider"; - float minimum = -100.0; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; float maximum = 100.0; - float step = 0.001; -> = 2.0; + float step = 0.1; +> = 100.0; +uniform bool Apply_To_Alpha_Layer = true; -#ifndef OPENGL -#define mat2 float2x2 -#endif +#define PI 3.1415927 +#define TWO_PI 6.283185 -mat2 rotate(float angle){ - return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); +#define PARTICLE_SIZE 0.009 + +#define PARTICLE_SCALE float2(0.5, 1.6) +#define PARTICLE_SCALE_VAR float2(0.25, 0.2) + +#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) +#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) + +#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 +#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 +#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 + +#define SIZE_MOD 1.05 +#define ALPHA_MOD 0.9 +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) +#define Movement_Speed Movement_Speed_Percent * 0.01 +#define UV float2(fragCoord.xy / uv_size) + +float hash1_2(float2 x) +{ + return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); } -float4 mainImage(VertData v_in) : TARGET +float2 hash2_2(float2 x) { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float d = distance(center_pos,v_in.uv); - if(d > power){ - return image.Sample(textureSampler, v_in.uv); + mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); + float2 y = mul(x, m); + return fract(sin(y) * 492.194); +} + +//Simple interpolated noise +float2 noise2_2(float2 uv) +{ + //float2 f = fract(uv); + float2 f = smoothstep(0.0, 1.0, fract(uv)); + + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + float2 v00 = hash2_2(uv00); + float2 v01 = hash2_2(uv01); + float2 v10 = hash2_2(uv10); + float2 v11 = hash2_2(uv11); + + float2 v0 = mix(v00, v01, f.y); + float2 v1 = mix(v10, v11, f.y); + float2 v = mix(v0, v1, f.x); + + return v; +} + +//Simple interpolated noise +float noise1_2(float2 uv) +{ + float2 f = fract(uv); + + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + + float v00 = hash1_2(uv00); + float v01 = hash1_2(uv01); + float v10 = hash1_2(uv10); + float v11 = hash1_2(uv11); + + float v0 = mix(v00, v01, f.y); + float v1 = mix(v10, v11, f.y); + float v = mix(v0, v1, f.x); + + return v; +} + +float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) +{ + float noise = 0.0; + float alpha = 1.0; + float size = 1.0; + float2 offset; + for (int i = 0; i < layers; i++) + { + offset += hash2_2(float2(alpha, size)) * 10.0; + + //Adding noise with movement + noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; + alpha *= alphaMod; + size *= sizeMod; } - float r = (cos(d*3.14159265359/power) +1)/2 * rotation; - float2 pos = v_in.uv - center_pos; - pos = mul(pos, rotate(r)); - pos += center_pos; - return image.Sample(textureSampler, pos); + + noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); + return noise; +} + +//Rotates point around 0,0 +float2 rotate(float2 vpoint, float deg) +{ + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + return mul(vpoint, m); +} + +//Cell center from point on the grid +float2 voronoiPointFromRoot(float2 root, float deg) +{ + float2 vpoint = hash2_2(root) - 0.5; + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + vpoint = mul(vpoint, m) * 0.66; + vpoint += root + 0.5; + return vpoint; +} + +//Voronoi cell point rotation degrees +float degFromRootUV(in float2 uv) +{ + return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; +} + +float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) +{ + return vpoint + (hash2_2(uv) - 0.5) * range; +} + + +float3 fireParticles(in float2 uv, in float2 originalUV) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float2 rootUV = floor(uv); + float deg = degFromRootUV(rootUV); + float2 pointUV = voronoiPointFromRoot(rootUV, deg); + float dist = 2.0; + float distBloom = 0.0; + + //UV manipulation for the faster particle movement + float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; + tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; + + //Sparks sdf + dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); + + //Bloom sdf + distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); + + //Add sparks + particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; + + //Add bloom + particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; + + //Upper disappear curve randomization + float border = (hash1_2(rootUV) - 0.5) * 2.0; + float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); + + //Lower appear curve randomization + border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; + float appear = smoothstep(border, border + 0.4, originalUV.y); + + return particles * disappear * appear; +} + + +//Layering particles to imitate 3D view +float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float size = 1.0; + float alpha = 1.0; + float2 offset = float2(0.0, 0.0); + float2 noiseOffset; + float2 bokehUV; + + for (int i = 0; i < layers; i++) + { + //Particle noise movement + noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; + + //UV with applied movement + bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; + + //Adding particles if there is more smoke, remove smaller particles + particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); + + //Moving uv origin to avoid generating the same particles + offset += hash2_2(float2(alpha, alpha)) * 10.0; + + alpha *= alphaMod; + size *= sizeMod; + } + + return particles; +} + + +void mainImage(out float4 fragColor, in float2 fragCoord) +{ + float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; + float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); + + uv *= 1.8; + float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); + + float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); + smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); + float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; + + //Cutting holes in smoke + smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), + 2.0) * 1.5; + + float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); + + float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); + col.rgb *= vignette; + col.rgb = smoothstep(-0.08, 1.0, col.rgb); + + if (Apply_To_Alpha_Layer) + { + float4 original_color = image.Sample(textureSampler, UV); + + float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min, 0.0, 1.0); + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } + + fragColor = col; +} + +/*ps end*/ + +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} + +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } } ' @@ -41385,73 +31723,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwoPassDropShadowShader { +function Get-OBSEmbossColorShader { -[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] +[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] param( -# Set the ViewProj of OBSTwoPassDropShadowShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSTwoPassDropShadowShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSTwoPassDropShadowShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSTwoPassDropShadowShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSTwoPassDropShadowShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSTwoPassDropShadowShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSTwoPassDropShadowShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSTwoPassDropShadowShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the shadow_offset_x of OBSTwoPassDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +# Set the Angle_Steps of OBSEmbossColorShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] [Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSTwoPassDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +$AngleSteps, +# Set the Radius_Steps of OBSEmbossColorShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] [Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSTwoPassDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +$RadiusSteps, +# Set the ampFactor of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('ampFactor')] +[Single] +$AmpFactor, +# Set the Up_Down_Percent of OBSEmbossColorShader +[Alias('Up_Down_Percent')] +[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] [Int32] -$ShadowBlurSize, -# Set the shadow_color of OBSTwoPassDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] -[String] -$ShadowColor, -# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader -[Alias('is_alpha_premultiplied')] -[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] +$UpDownPercent, +# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] [Management.Automation.SwitchParameter] -$IsAlphaPremultiplied, +$ApplyToAlphaLayer, +# Set the notes of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41482,126 +31785,89 @@ $UseShaderTime process { -$shaderName = 'two-pass-drop-shadow' -$ShaderNoun = 'OBSTwoPassDropShadowShader' +$shaderName = 'emboss_color' +$ShaderNoun = 'OBSEmbossColorShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} - -uniform int shadow_offset_x< - string label = "shadow offset x"; +// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; + int minimum = 1; + int maximum = 20; int step = 1; ->; -uniform int shadow_offset_y< - string label = "shadow offset y"; +> = 9; // +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; + int minimum = 0; + int maximum = 20; int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; +> = 4; // +uniform float ampFactor< + string label = "amp Factor"; string widget_type = "slider"; - int minimum = 0; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 12.0; +uniform int Up_Down_Percent< + string label = "Up Down Percent"; + string widget_type = "slider"; + int minimum = -100; int maximum = 100; int step = 1; ->; - -uniform float4 shadow_color; - -uniform bool is_alpha_premultiplied; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; float4 mainImage(VertData v_in) : TARGET { - int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float sampled_shadow_alpha = 0; - - for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (1 * uv_pixel_interval.y); + float maxRadius = (6 * uv_pixel_interval.y); -float4 mainImage_2_end(VertData v_in) : TARGET -{ - int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, - v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); - - float sampled_shadow_alpha = 0; - - for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) - { - float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); + float embossAngle = 0.25 * PI; -technique Draw -{ - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } - - pass p1 + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); + + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage_2_end(v_in); + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; + + for (float angle = 0; angle < (2 * PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; + + } + } + accumulatedColor *= ampFactor; + + c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); } + //return c0 + accumulatedColor; // down; + //return c0 - accumulatedColor; // up + return c0; } ' @@ -41701,37 +31967,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVCRShader { +function Get-OBSEmbossShader { -[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] +[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] param( -# Set the vertical_shift of OBSVCRShader -[Alias('vertical_shift')] -[ComponentModel.DefaultBindingProperty('vertical_shift')] -[Single] -$VerticalShift, -# Set the distort of OBSVCRShader -[ComponentModel.DefaultBindingProperty('distort')] -[Single] -$Distort, -# Set the vignet of OBSVCRShader -[ComponentModel.DefaultBindingProperty('vignet')] -[Single] -$Vignet, -# Set the stripe of OBSVCRShader -[ComponentModel.DefaultBindingProperty('stripe')] -[Single] -$Stripe, -# Set the vertical_factor of OBSVCRShader -[Alias('vertical_factor')] -[ComponentModel.DefaultBindingProperty('vertical_factor')] -[Single] -$VerticalFactor, -# Set the vertical_height of OBSVCRShader -[Alias('vertical_height')] -[ComponentModel.DefaultBindingProperty('vertical_height')] -[Single] -$VerticalHeight, +# Set the Use_Color of OBSEmbossShader +[Alias('Use_Color')] +[ComponentModel.DefaultBindingProperty('Use_Color')] +[Management.Automation.SwitchParameter] +$UseColor, +# Set the Apply_To_Alpha_Layer of OBSEmbossShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41762,108 +32011,42 @@ $UseShaderTime process { -$shaderName = 'VCR' -$ShaderNoun = 'OBSVCRShader' +$shaderName = 'emboss' +$ShaderNoun = 'OBSEmbossShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldjGzV -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float vertical_shift< - string label = "vertical shift"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 0.4; -uniform float distort< - string label = "distort"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 5.0; - float step = 0.001; -> = 1.2; -uniform float vignet< - string label = "vignet"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float stripe< - string label = "stripe"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_factor< - string label = "vertical factor"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_height< - string label = "vertical height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 30.0; - -float onOff(float a, float b, float c) -{ - return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); -} - -float ramp(float y, float start, float end) -{ - float inside = step(start,y) - step(end,y); - float fact = (y-start)/(end-start)*inside; - return (1.-fact) * inside; - -} +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform bool Use_Color; +uniform bool Apply_To_Alpha_Layer = true; -float modu(float x, float y) +float4 mainImage(VertData v_in) : TARGET { - return (x / y) - floor(x / y); -} -float stripes(float2 uv) -{ - return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; -} + float dx = 1 / uv_size.x; + float dy = 1 / uv_size.y; -float4 getVideo(float2 uv) -{ - float2 look = uv; - float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); - look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; - float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + - (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); - look.y = modu((look.y + vShift) , 1.); - return image.Sample(textureSampler, look); -} + float4 c0 = image.Sample(textureSampler, v_in.uv); + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); + float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); + float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); + float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); + float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); + float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); -float2 screenDistort(float2 uv) -{ - uv -= float2(.5,.5); - uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); - uv += float2(.5,.5); - return uv; -} + c0 = (-c1 - c2 - c4 + c6 + c8 + c9); + float c = (c0.r + c0.g + c0.b) / 3 + 0.5; + c0 = float4(c,c,c,c); -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv = screenDistort(uv); - float4 video = getVideo(uv); - float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); - float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; - video += stripes(uv); - video *= vignette; - video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; - return float4(video.r, video.g, video.b ,1.0); + if (Use_Color) + { + float4 rgba = image.Sample(textureSampler, v_in.uv); + return (0.5 * rgba) + c0; + } + } + return c0; } ' @@ -41963,55 +32146,60 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVHSShader { +function Get-OBSExeldroBentCameraShader { -[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] +[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] param( -# Set the range of OBSVHSShader -[ComponentModel.DefaultBindingProperty('range')] +# Set the left_side_width of OBSExeldroBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] [Single] -$Range, -# Set the offsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('offsetIntensity')] +$LeftSideWidth, +# Set the left_side_size of OBSExeldroBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] [Single] -$OffsetIntensity, -# Set the noiseQuality of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseQuality')] +$LeftSideSize, +# Set the left_side_shadow of OBSExeldroBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] [Single] -$NoiseQuality, -# Set the noiseIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseIntensity')] +$LeftSideShadow, +# Set the left_flip_width of OBSExeldroBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] [Single] -$NoiseIntensity, -# Set the colorOffsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] +$LeftFlipWidth, +# Set the left_flip_shadow of OBSExeldroBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] [Single] -$ColorOffsetIntensity, -# Set the Alpha_Percentage of OBSVHSShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +$LeftFlipShadow, +# Set the right_side_width of OBSExeldroBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] [Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSVHSShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSVHSShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSVHSShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSVHSShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, +$RightSideWidth, +# Set the right_side_size of OBSExeldroBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] +[Single] +$RightSideSize, +# Set the right_side_shadow of OBSExeldroBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] +[Single] +$RightSideShadow, +# Set the right_flip_width of OBSExeldroBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] +[Single] +$RightFlipWidth, +# Set the right_flip_shadow of OBSExeldroBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42042,138 +32230,127 @@ $UseShaderTime process { -$shaderName = 'VHS' -$ShaderNoun = 'OBSVHSShader' +$shaderName = 'exeldro-bent-camera' +$ShaderNoun = 'OBSExeldroBentCameraShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 -//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 -//Converted to OpenGL by Exeldro February 19, 2022 -//Use improved input fields by Exeldro April 15, 2023 -uniform float range< - string label = "Wave size (0.05)"; +uniform float left_side_width< + string label = "Left side width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 0.20; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; float step = 0.01; > = 0.05; -uniform float offsetIntensity< - string label = "Offset intensity (0.02)"; +uniform float left_flip_shadow< + string label = "Left flip shadow"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 0.20; + float minimum = 0.0; + float maximum = 1.0; float step = 0.01; -> = 0.02; -uniform float noiseQuality< - string label = "Noise number of lines (250)"; +> = 0.6; + +uniform float right_side_width< + string label = "Right side width"; string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 10.0; -> = 250.0; -uniform float noiseIntensity< - string label = "Noise intensity (0.88)"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; + float maximum = 1.0; float step = 0.01; -> = 0.88; -uniform float colorOffsetIntensity< - string label = "Color offset intensity (1.3)"; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 1.3; -uniform float Alpha_Percentage< - string label = "Aplha percentage (100.0)"; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color; - -float dot2(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} - -float rand(float2 co) -{ - return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); -} - -float verticalBar(float pos, float uvY, float offset) -{ - float edge0 = (pos - range); - float edge1 = (pos + range); - - float x = smoothstep(edge0, pos, uvY) * offset; - x -= smoothstep(pos, edge1, uvY) * offset; - return x; -} - -float modu(float x, float y) -{ - return (x / y) - floor(x / y); -} - -float dot4(float4 a,float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; -} + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - for (float i = 0.0; i < 0.71; i += 0.1313) - { - float d = modu(elapsed_time * i, 1.7); - float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); - o *= offsetIntensity; - uv.x += verticalBar(d, uv.y, o); + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width / 2.0; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width / 2.0; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; } - float uvY = uv.y; - uvY *= noiseQuality; - uvY = float(int(uvY)) * (1.0 / noiseQuality); - float noise = rand(float2(elapsed_time * 0.00001, uvY)); - uv.x += noise * noiseIntensity / 100.0; - float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; - float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; - - float4 rgba = image.Sample(textureSampler, uv); - float r = image.Sample(textureSampler, uv + offsetR).r; - float g = image.Sample(textureSampler, uv + offsetG).g; - float b = rgba.b; - - rgba = float4(r, g, b, rgba.a); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); - if (Replace_Image_Color) - color = float4(luma,luma,luma,luma); - rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; } - - return rgba; + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -42271,26 +32448,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVignettingShader { +function Get-OBSFadeTransitionShader { -[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] +[Alias('Set-OBSFadeTransitionShader','Add-OBSFadeTransitionShader')] param( -# Set the innerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('innerRadius')] -[Single] -$InnerRadius, -# Set the outerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('outerRadius')] -[Single] -$OuterRadius, -# Set the opacity of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, -# Set the notes of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the image_a of OBSFadeTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] [String] -$Notes, +$ImageA, +# Set the image_b of OBSFadeTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSFadeTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSFadeTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42321,62 +32502,30 @@ $UseShaderTime process { -$shaderName = 'vignetting' -$ShaderNoun = 'OBSVignettingShader' +$shaderName = 'fade-transition' +$ShaderNoun = 'OBSFadeTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -uniform float innerRadius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.9; -uniform float outerRadius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.5; -uniform float opacity< - string label = "opacity"; +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; -> = 0.8; -uniform string notes< - string widget_type = "info"; -> = "inner radius will always be shown, outer radius is the falloff"; +> = 0.5; +uniform bool convert_linear = true; float4 mainImage(VertData v_in) : TARGET { - float PI = 3.1415926535897932384626433832795;//acos(-1); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; - - float xTrans = (v_in.uv.x * 2) - 1; - float yTrans = 1 - (v_in.uv.y * 2); - - float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); - - float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); - float factor = 1 - subtraction; - - float4 vignetColor = c0 * factor; - vignetColor *= verticalDim; - - vignetColor *= opacity; - c0 *= 1-opacity; - - float4 output_color = c0 + vignetColor; - - return float4(output_color); + float4 a_val = image_a.Sample(textureSampler, v_in.uv); + float4 b_val = image_b.Sample(textureSampler, v_in.uv); + float4 rgba = lerp(a_val, b_val, transition_time); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -42474,18 +32623,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVoronoiPixelationShader { +function Get-OBSFillColorGradientShader { -[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] +[Alias('Set-OBSFillColorGradientShader','Add-OBSFillColorGradientShader')] param( -# Set the pixH of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('pixH')] +# Set the Fill of OBSFillColorGradientShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$PixH, -# Set the alternative of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('alternative')] -[Management.Automation.SwitchParameter] -$Alternative, +$Fill, +# Set the Gradient_Width of OBSFillColorGradientShader +[Alias('Gradient_Width')] +[ComponentModel.DefaultBindingProperty('Gradient_Width')] +[Single] +$GradientWidth, +# Set the Gradient_Offset of OBSFillColorGradientShader +[Alias('Gradient_Offset')] +[ComponentModel.DefaultBindingProperty('Gradient_Offset')] +[Single] +$GradientOffset, +# Set the Fill_Direction of OBSFillColorGradientShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill_Color of OBSFillColorGradientShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42516,99 +32681,83 @@ $UseShaderTime process { -$shaderName = 'voronoi-pixelation' -$ShaderNoun = 'OBSVoronoiPixelationShader' +$shaderName = 'fill_color_gradient' +$ShaderNoun = 'OBSFillColorGradientShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro +uniform float Fill< + string label = "Fill"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 1; + float step = 0.005; +> = 0.500; -uniform float pixH< - string label = "Size"; +uniform float Gradient_Width< + string label = "Gradient Width"; string widget_type = "slider"; - float minimum = 4.0; - float maximum = 500.0; + float minimum = 0; + float maximum = 0.15; // Adjust the maximum value as needed float step = 0.01; -> = 100.0; -uniform bool alternative; +> = 0.05; -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); -} +uniform float Gradient_Offset< + string label = "Gradient Offset"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 0.100; // Adjust the maximum value as needed + float step = 0.005; +> = 0.00; -float2 random2( float2 p ) { - return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); -} -float2 randomSpin(float2 p, float f){ - return 1.0 * float2( - cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), - sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); -} -float4 VoronoiPixelation(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); +uniform int Fill_Direction< + string label = "Fill from:"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Left"; + int option_1_value = 1; + string option_1_label = "Right"; + int option_2_value = 2; + string option_2_label = "Bottom"; + int option_3_value = 3; + string option_3_label = "Top"; +> = 0; +uniform float4 Fill_Color; - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); +float4 mainImage(VertData v_in) : TARGET +{ + float distanceToEdge = 0.0; - float2 point1 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point1 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; - } - } - } - float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x ; - - return image.Sample(textureSampler, nPoint); -} -float4 VoronoiPixelation2(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); + // Calculate distance to the fill edge based on the selected direction + if (Fill_Direction == 0) + distanceToEdge = Fill - v_in.uv.x; + else if (Fill_Direction == 1) + distanceToEdge = v_in.uv.x - (1.0 - Fill); + else if (Fill_Direction == 2) + distanceToEdge = v_in.uv.y - (1.0 - Fill); + else if (Fill_Direction == 3) + distanceToEdge = Fill - v_in.uv.y; + // Calculate the gradient factor based on the distance to the edge and the gradient width + float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); + float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); + // Adjust distanceToEdge by the Gradient_Offset + distanceToEdge += gradientOffset; - float2 point2 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point2 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; - } - } - } - float2 nPoint = pixExt + relClos + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x; - - return image.Sample(textureSampler, nPoint); -} + // Normalize the distance to be between 0 and 1 + distanceToEdge = saturate(distanceToEdge); + // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); + // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); + float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); -float4 mainImage(VertData v_in) : TARGET -{ - if (alternative) { - return VoronoiPixelation2(v_in.uv, pixH); - } else { - return VoronoiPixelation(v_in.uv, pixH); - } + // Blend between the fill color and the original image color using the gradient factor + float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); + + return finalColor; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -42706,49 +32855,25 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZigZagShader { +function Get-OBSFillColorLinearShader { -[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] +[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] param( -# Set the radius of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the angle of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the period of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('period')] -[Single] -$Period, -# Set the amplitude of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the center_x of OBSZigZagShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSZigZagShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the phase of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('phase')] +# Set the Fill of OBSFillColorLinearShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$Phase, -# Set the animate of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('animate')] +$Fill, +# Set the Fill_Direction of OBSFillColorLinearShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] [Int32] -$Animate, -# Set the notes of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time +$FillDirection, +# Set the Fill_Color of OBSFillColorLinearShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] [String] @@ -42778,121 +32903,57 @@ $UseShaderTime process { -$shaderName = 'ZigZag' -$ShaderNoun = 'OBSZigZagShader' +$shaderName = 'fill_color_linear' +$ShaderNoun = 'OBSFillColorLinearShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.0; -uniform float angle< - string label = "angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 180.0; -uniform float period< - string label = "period"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.5; -uniform float amplitude< - string label = "amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; - -uniform float center_x< - string label = "center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; -uniform float center_y< - string label = "center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; - -uniform float phase< - string label = "phase"; +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform int animate< - string label = "animate"; + float minimum = 0; + float maximum = 1; + float step = 0.005; +>; +uniform int Fill_Direction< + string label = "Fill from:"; string widget_type = "select"; int option_0_value = 0; - string option_0_label = "No"; + string option_0_label = "Left"; int option_1_value = 1; - string option_1_label = "Amplitude"; + string option_1_label = "Right"; int option_2_value = 2; - string option_2_label = "Time"; + string option_2_label = "Top"; + int option_3_value = 3; + string option_3_label = "Bottom"; > = 0; - - -uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." - +uniform float4 Fill_Color; float4 mainImage(VertData v_in) : TARGET { - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; + bool is_inside_fill = true; - float dist = distance(v_out.uv, center); - if (dist < radius) + // Check if the pixel is within the specified "fill width" on the left side + if(Fill_Direction == 0){ + is_inside_fill = v_in.uv.x > Fill; + } + if(Fill_Direction == 1) { - float percent = (radius-dist)/radius; - float theta = percent * percent * - ( - animate == 1 ? - amplitude * sin(elapsed_time) : - amplitude - ) - * sin(percent * percent / period * radians(angle) + (phase + - ( - animate == 2 ? - elapsed_time : - 0 - ))); - - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; - - return image.Sample(textureSampler, v_out.uv); + is_inside_fill = v_in.uv.x < (1.0 - Fill); } - else + if(Fill_Direction == 2) { - return image.Sample(textureSampler, v_in.uv); + is_inside_fill = v_in.uv.y > Fill; + } + if(Fill_Direction == 3) + { + is_inside_fill = v_in.uv.y < (1.0 - Fill); } - + + // Invert is_inside_fill + is_inside_fill = !is_inside_fill; + + // If inside the "fill," make the pixel selected colour; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } ' } @@ -42991,35 +33052,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomBlurShader { +function Get-OBSFillColorRadialDegreesShader { -[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] +[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] param( -# Set the samples of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('samples')] +# Set the Fill_Direction of OBSFillColorRadialDegreesShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] [Int32] -$Samples, -# Set the magnitude of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('magnitude')] +$FillDirection, +# Set the Fill of OBSFillColorRadialDegreesShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$Magnitude, -# Set the speed_percent of OBSZoomBlurShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the ease of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('notes')] +$Fill, +# Set the Start_Angle of OBSFillColorRadialDegreesShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] +[Single] +$StartAngle, +# Set the Offset_X of OBSFillColorRadialDegreesShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialDegreesShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialDegreesShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] [String] -$Notes, +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43050,106 +33115,95 @@ $UseShaderTime process { -$shaderName = 'zoom_blur' -$ShaderNoun = 'OBSZoomBlurShader' +$shaderName = 'fill_color_radial_degrees' +$ShaderNoun = 'OBSFillColorRadialDegreesShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl -//for Media Player Classic HC or BE -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int samples < - string label = "Samples"; +#define PI 3.141592653589793238 + +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; + +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 32; -uniform float magnitude< - string label = "Magnitude"; + float minimum = 0; + float maximum = 360; + float step = 1.00000; +>; + +uniform float Start_Angle< + string label = "Start Angle"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform int speed_percent < - string label = "Speed percent"; + float minimum = 0; + float maximum = 720; + float step = 1.00000; +> = 360.0; + +uniform float Offset_X< + string label = "Offset X"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform bool glitch; -uniform string notes< - string widget_type = "info"; -> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} +uniform float Offset_Y< + string label = "Offset Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; -} +uniform float4 Fill_Color; float4 mainImage(VertData v_in) : TARGET { - float speed = speed_percent * 0.01; - - // circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); - - b = Styler(t, 0, c, d, ease); - float sample_speed = max(samples * b, 1.0); + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - float PI = 3.1415926535897932384626433832795;//acos(-1); - float4 c0 = image.Sample(textureSampler, v_in.uv); + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - float xTrans = (v_in.uv.x*2)-1; - float yTrans = 1-(v_in.uv.y*2); - - float angle = atan(yTrans/xTrans) + PI; - if (sign(xTrans) == 1) { - angle+= PI; - } - float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; - float2 currentCoord; - float4 accumulatedColor = float4(0,0,0,0); + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor = currentColor; + // Convert angle from radians to degrees + angle = degrees(angle); - accumulatedColor = c0/sample_speed; - for(int i = 1; i< sample_speed; i++) { - float currentRadius ; - // Distance to center dependent - currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; - // Continuous; - // currentRadius = max(0,radius - (0.0004 * i)); + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; + } - currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; - currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor += currentColor/sample_speed; - - } + // Check if the angle is within the specified "fill width" + bool is_inside_fill = angle < Fill; - return accumulatedColor; + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } ' @@ -43249,24 +33303,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomShader { +function Get-OBSFillColorRadialPercentageShader { -[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] +[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] param( -# Set the center_x_percent of OBSZoomShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] +# Set the Fill_Direction of OBSFillColorRadialPercentageShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] [Int32] -$CenterYPercent, -# Set the power of OBSZoomShader -[ComponentModel.DefaultBindingProperty('power')] +$FillDirection, +# Set the Fill of OBSFillColorRadialPercentageShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$Power, +$Fill, +# Set the Start_Angle of OBSFillColorRadialPercentageShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] +[Single] +$StartAngle, +# Set the Offset_X of OBSFillColorRadialPercentageShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialPercentageShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialPercentageShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43297,40 +33366,100 @@ $UseShaderTime process { -$shaderName = 'zoom' -$ShaderNoun = 'OBSZoomShader' +$shaderName = 'fill_color_radial_percentage' +$ShaderNoun = 'OBSFillColorRadialPercentageShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; +#define PI 3.141592653589793238 + +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; + +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float power< - string label = "power"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.00005; +> = 0.0; + +uniform float Start_Angle< + string label = "Start Angle"; string widget_type = "slider"; float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1.75; + float maximum = 720; + float step = 1.00000; +> = 360.0; + +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float Offset_Y< + string label = "Offset Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float4 Fill_Color; float4 mainImage(VertData v_in) : TARGET { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; - return image.Sample(textureSampler, uv); + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); + + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); + + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; + + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); + + // Convert angle from radians to degrees + angle = degrees(angle); + + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; + + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; + } + + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; + + // Calculate the percentage of the angle + float anglePercentage = angle / 360.0; + + // Check if the angle percentage is within the specified "fill percentage" + bool is_inside_fill = anglePercentage < Fill; + + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -43428,30 +33557,71 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomXYShader { +function Get-OBSFilterTemplateShader { -[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] +[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] param( -# Set the center_x_percent of OBSZoomXYShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomXYShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the x_power of OBSZoomXYShader -[Alias('x_power')] -[ComponentModel.DefaultBindingProperty('x_power')] +# Set the ViewProj of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSFilterTemplateShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$XPower, -# Set the y_power of OBSZoomXYShader -[Alias('y_power')] -[ComponentModel.DefaultBindingProperty('y_power')] +$ElapsedTime, +# Set the uv_offset of OBSFilterTemplateShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSFilterTemplateShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSFilterTemplateShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSFilterTemplateShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSFilterTemplateShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$YPower, +$RandF, +# Set the rand_instance_f of OBSFilterTemplateShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSFilterTemplateShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSFilterTemplateShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43482,54 +33652,75 @@ $UseShaderTime process { -$shaderName = 'Zoom_XY' -$ShaderNoun = 'OBSZoomXYShader' +$shaderName = 'filter_template' +$ShaderNoun = 'OBSFilterTemplateShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Zoom XY Shader +//My shader modified by Me for use with obs-shaderfilter month/year v.02 -// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ +//Section to converting GLSL to HLSL - can delete if unneeded +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) -// The allow for an independent Horizontal and Vertical Zoom. +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** -uniform int center_x_percent< - string label = "center x percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float x_power< - string label = "x power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ +uniform string notes< + string widget_type = "info"; +> = "add notes here"; -uniform float y_power< - string label = "y power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; float4 mainImage(VertData v_in) : TARGET { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; - return image.Sample(textureSampler, uv); + return image.Sample(textureSampler, v_in.uv); +} + +/* +**Shaders use the built in Draw technique** +**this section can be deleted** + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } +*/ + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -43627,1710 +33818,5412 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Set-OBS3DFilter { - - - [Alias('Add-OBS3DFilter')] - param( - # The Field of View - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fov")] - [double] - $FieldOfView, +function Get-OBSFire3Shader { - # The Rotation along the X-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rot_x")] - [double] - $RotationX, +[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] +param( +# Set the ViewProj of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSFire3Shader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSFire3Shader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSFire3Shader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSFire3Shader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSFire3Shader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSFire3Shader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSFire3Shader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSFire3Shader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSFire3Shader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the Movement_Direction_Horizontal of OBSFire3Shader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +[Single] +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSFire3Shader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Alpha_Percentage of OBSFire3Shader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Invert of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFire3Shader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFire3Shader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Color_To_Replace of OBSFire3Shader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Apply_To_Specific_Color of OBSFire3Shader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Full_Width of OBSFire3Shader +[Alias('Full_Width')] +[ComponentModel.DefaultBindingProperty('Full_Width')] +[Management.Automation.SwitchParameter] +$FullWidth, +# Set the Flame_Size of OBSFire3Shader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Single] +$FlameSize, +# Set the Spark_Grid_Height of OBSFire3Shader +[Alias('Spark_Grid_Height')] +[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] +[Single] +$SparkGridHeight, +# Set the Flame_Modifier of OBSFire3Shader +[Alias('Flame_Modifier')] +[ComponentModel.DefaultBindingProperty('Flame_Modifier')] +[Single] +$FlameModifier, +# Set the Flame_Tongue_Size of OBSFire3Shader +[Alias('Flame_Tongue_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] +[Single] +$FlameTongueSize, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The Rotation along the Y-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rot_y")] - [double] - $RotationY, - # The Rotation along the Z-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rot_z")] - [double] - $RotationZ, +process { +$shaderName = 'fire-3' +$ShaderNoun = 'OBSFire3Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//My effect modified by Me for use with obs-shaderfilter month/year v.02 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; - # The Position along the X-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("pos_x")] - [double] - $PositionX, +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp - # The Position along the Y-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("pos_y")] - [double] - $PositionY, - # The Position along the Z-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("pos_z")] - [double] - $PositionZ, +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; - # The scale of the source along the X-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("scale_x")] - [double] - $ScaleX, +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; - # The scale of the source along the Y-axis - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("scale_y")] - [double] - $ScaleY, +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' +uniform int Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 80; +uniform bool Invert = false; +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.04; +uniform bool Apply_To_Image = true; +uniform bool Replace_Image_Color = true; +uniform float4 Color_To_Replace; +uniform bool Apply_To_Specific_Color = false; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "3Band3D" - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "3d_effect_filter" - filterSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float2 uv = v_in.uv; + if(Invert) + uv = 1.0 - v_in.uv; + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } +int2 iMouse() +{ + return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); +} - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } +float mod(float x, float y) +{ + return x - y * floor(x / y); +} - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - - } - - } +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x / y); } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSColorFilter { - - - [Alias('Add-OBSColorFilter','Add-OBSColorCorrectionFilter','Set-OBSColorCorrectionFilter')] - param( - # The opacity, as a number between 0 and 1. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(0,1)] - [ComponentModel.DefaultBindingProperty("opacity")] - [double] - $Opacity, +/*ps start*/ +#define PI 3.1415926535897932384626433832795 +uniform bool Full_Width = false; - # The brightness, as a number between -1 and 1. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-1,1)] - [ComponentModel.DefaultBindingProperty("brightness")] - [double] - $Brightness, +uniform float Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; - # The constrast, as a number between -4 and 4. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-4,4)] - [ComponentModel.DefaultBindingProperty("contrast")] - [double] - $Contrast, +uniform float Spark_Grid_Height< + string label = "Spark Grid Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; - # The gamma correction, as a number between -3 and 3. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-3,3)] - [ComponentModel.DefaultBindingProperty("gamma")] - [double] - $Gamma, +uniform float Flame_Modifier< + string label = "Flame Modifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # The saturation, as a number between -1 and 5. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-1,5)] - [ComponentModel.DefaultBindingProperty("saturation")] - [double] - $Saturation, +uniform float Flame_Tongue_Size< + string label = "Flame Tongue Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 8.5; - # The change in hue, as represented in degrees around a color cicrle - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("hue_shift")] - [Alias('Spin')] - [double] - $Hue, +// +// Description : Array and textureless GLSL 2D/3D/4D simplex +// noise functions. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : ijm +// Lastmod : 20110822 (ijm) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. +// https://github.com/ashima/webgl-noise +// - # Multiply this color by all pixels within the source. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_multiply")] - [string] - $MultiplyColor, +vec3 mod2893(vec3 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} - # Add all this color to all pixels within the source. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_add")] - [string] - $AddColor, +vec4 mod289(vec4 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' +vec4 permute(vec4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} +vec4 taylorInvSqrt(vec4 r) +{ + return 1.79284291400159 - 0.85373472095314 * r; +} - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +float snoise(vec3 v) +{ + const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); - } - begin { - filter ToOBSColor { - - if ($_ -is [uint32]) { $_ } - elseif ($_ -is [string]) { - if ($_ -match '^\#[a-f0-9]{3,4}$') { - $_ = $_ -replace '[a-f0-9]','$0$0' - } - - if ($_ -match '^#[a-f0-9]{8}$') { - $_ -replace '#','0x' -as [UInt32] - } - elseif ($_ -match '^#[a-f0-9]{6}$') { - $_ -replace '#','0xff' -as [UInt32] - } - } - - } - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $FilterName = $myParameters["FilterName"] = "ColorCorrection" - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +// First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +// Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } - - if ($myParameterData.color_add) { - $myParameterData.color_add = $myParameterData.color_add | ToOBSColor - } + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y - if ($myParameterData.color_multiply) { - $myParameterData.color_multiply = $myParameterData.color_multiply | ToOBSColor - } +// Permutations + i = mod2893(i); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "color_filter_v2" - filterSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } +// Gradients: 7x7 points over a square, mapped onto an octahedron. +// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) - if ($PassThru) { - return $outputAddedResult - } + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - } - - } -} + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSEqualizerFilter { - - - [Alias('Add-OBSEqualizierFilter','Add-OBS3BandEqualizerFilter','Set-OBS3BandEqualizerFilter')] - param( - # The change in low frequencies. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("low")] - [ValidateRange(-20,20)] - [double] - $Low, + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); - # The change in mid frequencies. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("mid")] - [ValidateRange(-20,20)] - [double] - $Mid, + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; - # The change in high frequencies. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("high")] - [ValidateRange(-20,20)] - [double] - $High, + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' +//Normalise gradients + //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; +// Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); +} - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +////////////////////////////////////////////////////////////// - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "3BandEqualizer" - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +// PRNG +// From https://www.shadertoy.com/view/4djSRW +float prng(in vec2 seed) +{ + seed = fract(seed * vec2(5.3983, 5.4427)); + seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); + return fract(seed.x * seed.y * 95.4337); +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +////////////////////////////////////////////////////////////// - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "basic_eq_filter" - filterSettings = $myParameterData - } - - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } - - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - +float noiseStack(vec3 pos, int octaves, float falloff) +{ + float noise = snoise(vec3(pos)); + float off = 1.0; + if (octaves > 1) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 2) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 3) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + return (1.0 + noise) / 2.0; +} - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } +vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) +{ + float displaceA = noiseStack(pos, octaves, falloff); + float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); + return vec2(displaceA, displaceB); +} - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 UV = (1.0 - v_in.uv) * uv_scale; + if (Invert) + UV = v_in.uv * uv_scale; + float alpha = saturate(Alpha_Percentage * .01); + float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); + vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); + if (Full_Width) + { + resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; + } -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSGainFilter { + resolution.x = mul(resolution.x, 1 / 1); + float time = iTime * (Speed * 0.01); + //vec2 drag = iMouse().xy; + vec2 offset = iMouse().xy; + // + float xpart = UV.x / resolution.x; + float ypart = UV.y / resolution.y; + // + + float ypartClip = UV.y / ( flame_size * 75.0); + float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); + float ypartClipped = min(ypartClip, 1.0); + float ypartClippedn = (1 - ypartClipped); + // + float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); + // + float timeSpeed = 0.5 * (Speed * 0.01); + float realTime = -1.0 * timeSpeed * time; + // + vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; + vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); + vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); + vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; + // + vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); + vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); + // + vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; + float noise = noiseStack(noiseCoord, 3, 0.4); + // + float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); + // + float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); + float fff = f * f * f; + vec3 fire = 1.5 * vec3(f, fff, fff * fff); + // + // smoke + float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; + float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); + vec3 smoke = vec3(smokePart, smokePart, smokePart); + // + // sparks + float sparkGridSize = Spark_Grid_Height; + vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); + sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); + sparkCoord += 100.0 * flow.xy; + if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) + sparkCoord.x += 0.5 * sparkGridSize; + vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); + float sparkRandom = prng( sparkGridIndex); + float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); + vec3 sparks = vec3(0.0, 0.0, 0.0); + if (sparkLife > 0.0) + { + float sparkSize = xfuel * xfuel * sparkRandom * 0.08; + float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; + vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); + vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; + vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); + float sparkLength = length(sparkModulus); + float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); + sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); + } + // + float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); - - [Alias('Add-OBSGainFilter')] - param( - # The Audio Gain, in decibels. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("db")] - [double] - $Gain, - - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' - - - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } + // remove dark areas per user + float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire, 0.0, alpha); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } + else + { + rgba = color; } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) + } - $DynamicParameters - + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "Gain" - } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "gain_filter" - filterSettings = [Ordered]@{db=$Gain} - NoResponse = $myParameters["NoResponse"] - } - - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } + + return rgba; +} - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - } -} - - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSRenderDelayFilter { - - - [Alias('Add-OBSRenderDelayFilter')] - param( - # The RenderDelay. - [Parameter(ValueFromPipelineByPropertyName)] - [timespan] - $RenderDelay, + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "RenderDelay" - } - - - $myParameterData = [Ordered]@{ - delay_ms = if ($RenderDelay.Ticks -lt 10kb) { - [int]$RenderDelay.Ticks - } else { - [int]$RenderDelay.TotalMilliseconds - } - } - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "gpu_delay" - filterSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } +} + + +} #.ExternalHelp obs-powershell-Help.xml -function Set-OBSScaleFilter { - - - [Alias('Add-OBSScaleFilter')] - param( - # The Resolution. Can either width x height (e.g. 1920x1080) or an aspect ratio (16:9). - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("resolution")] - [Alias('Scale')] - [string] - $Resolution, +function Get-OBSFireShader { + +[Alias('Set-OBSFireShader','Add-OBSFireShader')] +param( +# Set the Alpha_Percentage of OBSFireShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFireShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Flame_Size of OBSFireShader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Int32] +$FlameSize, +# Set the Fire_Type of OBSFireShader +[Alias('Fire_Type')] +[ComponentModel.DefaultBindingProperty('Fire_Type')] +[Int32] +$FireType, +# Set the Invert of OBSFireShader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFireShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFireShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSFireShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSFireShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSFireShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'fire' +$ShaderNoun = 'OBSFireShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 +// https://github.com/Oncorporation/obs-shaderfilter plugin +// https://www.shadertoy.com/view/MtcGD7 original version +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +//v.5 +// flicker +// flame type +// apply to image +// replace image color +// speed +// flame size +// alpha +// invert direction/position + + +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp + +uniform int Alpha_Percentage< + string label = "Aplha Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 100; +uniform int Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 70; +uniform int Fire_Type< + string label = "Fire Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Smaller and more whisps"; + int option_1_value = 1; + string option_1_label = "Larger and more volume"; +> = 1; + +uniform bool Invert < + string name = "Invert"; +> = false; +uniform float lumaMin< + string label = "Luma min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma min smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.04; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; + +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +float rand(vec2 n) +{ + return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); + //return sin(rand_f, n); +} + +float noise(vec2 n) +{ + const vec2 d = vec2(0.0, 1.0); + vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); + return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); +} + +float fbm(vec2 n) +{ + float total = 0.0, amplitude = 1.0; + for (int i = 0; i < 5; i++) + { + total += noise(n) * amplitude; + n += n * 1.7; + amplitude *= 0.47; + } + return total; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale; + float flame_size = clamp(Flame_Size * .01,-5,5); + + // inverting direction is logically inverted to allow the bottom up to be normal + float fire_base = (v_in.uv.y / iResolution.y); + float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); + float direction = -1.0 * clamp(Speed*.01,-5,5); + if (!Invert) + { + direction *= -1.0; + fire_base = 1 - fire_base; + fire_pix = 1 - fire_pix; + } + float iTime = direction * elapsed_time; + + const vec3 c1 = vec3(0.5, 0.0, 0.1); + const vec3 c2 = vec3(0.9, 0.1, 0.0); + const vec3 c3 = vec3(0.2, 0.1, 0.7); + const vec3 c4 = vec3(1.0, 0.9, 0.1); + const vec3 c5 = vec3(0.1, 0.1, 0.1); + const vec3 c6 = vec3(0.9, 0.9, 0.9); + + vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); + float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; + float alpha = saturate(Alpha_Percentage * .01); + + //change the constant term for all kinds of cool distance versions, + //make plus/minus to switch between + //ground fire and fire rain! + float dist = 3.5 - sin(iTime * 0.4) / 1.89; + + vec2 p = fire_pix * dist / iResolution.xx; + p.x -= iTime / 1.1; + float3 black = float3(0,0,0); + vec3 fire; + + if (Fire_Type == 1) + { + //fire version 1 larger and more volume + float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); + float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); + float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; + float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; + q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; + + vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), + fbm(p - q - iTime* speed.y)) ; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); + + fire += .05; + fire.r *= .8; + vec3 hsv = rgb2hsv(fire); + hsv.y *= hsv.z * 1.1; + hsv.z *= hsv.y * 1.13; + hsv.y = (2.2 - hsv.z * .9) * 1.20; + fire = hsv2rgb(hsv); + } + else + { + // fire version 0 - smaller and more whisps + p += (rand_f *.01); + float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); + float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); + float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; + float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; + q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; + + vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), + fbm(p + q - iTime * speed.y)) * shift; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); + + fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); + fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); + fire = fire/(1.0+max(black,fire)); + } + float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); + + // remove dark areas per user + float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire,0.0,alpha); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); + } + else + { + rgba = color; + } + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + return rgba; +} + + + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFireworks2Shader { + +[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] +param( +# Set the Speed of OBSFireworks2Shader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'fireworks2' +$ShaderNoun = 'OBSFireworks2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// based on https://www.shadertoy.com/view/4dBGRw + +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 1.0; +> = 100.0; + +#ifndef OPENGL +#define mat2 float2x2 +#define mix lerp +float mod(float x, float y) +{ + return x - y * floor(x / y); +} +#endif +//Creates a diagonal red-and-white striped pattern. +float3 barberpole(float2 pos, float2 rocketpos){ + float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); + float3 col=float3(1.0,1.0,1.0); + + d = mod(d*20.,2.0); + if(d>1.0){ + col=float3(1.0,0.0,0.0); + } + return col; +} + +float4 rocket(float2 pos, float2 rocketpos){ + float4 col = float4(0.0,0.0,0.0,0.0); + float f = 0.; + float absx= abs(rocketpos.x - pos.x); + float absy = abs(rocketpos.y-pos.y); + //wooden stick + if(absx<0.01&&absy<0.22){ + col=float4(1.0,0.5,0.5,1.0); + } + + //Barberpole + + if(absx<0.05&&absy<0.15){ + col=float4(barberpole(pos, rocketpos),1.0); + } + //Rocket Point + float pointw=(rocketpos.y-pos.y-0.25)*-0.7; + if((rocketpos.y-pos.y)>0.1){ + f=smoothstep(pointw-0.001,pointw+0.001,absx); + + col=mix(float4(1.0,0.0,0.0,1.0),col, f); + } + //Shadow + + f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); + col.rgb *= 0.7+f; + + return col; +} + + + +float rand(float val, float seed){ + return cos(val*sin(val*seed)*seed); +} + +float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } + + + +float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ + float4 col= float4(0.0,0.0,0.0,0.0); + float2 pp = float2(1.0,0.0); + mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); + for(float i=1.0;i<=128.0;i++){ + float d=rand(i, seed); + float fade=(i/128.0)*time; + float2 particpos = cpos + time*pp*d; + pp = mul(rr,pp); + col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); + } + col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); + col.a = col.r+col.g+col.b; + return col; +} +float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ + + float timeoffset = 2.0; + float4 col=float4(0.0,0.0,0.0,0.0); + if(time<=0.){ + return col; + } + time *= Speed /100.0; + if(mod(time, 6.0)>timeoffset){ + col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); + }else{ + + col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); + } + return col; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; + uv.y = -uv.y; + uv.x *= uv_size.x/uv_size.y; + float4 col = image.Sample(textureSampler, v_in.uv); + //col.rgb += 0.1*uv.y; + float4 c; + c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); + col = mix(col, c, c.a); + + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFireworksShader { + +[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] +param( +# Set the show_flash of OBSFireworksShader +[Alias('show_flash')] +[ComponentModel.DefaultBindingProperty('show_flash')] +[Management.Automation.SwitchParameter] +$ShowFlash, +# Set the show_stars of OBSFireworksShader +[Alias('show_stars')] +[ComponentModel.DefaultBindingProperty('show_stars')] +[Management.Automation.SwitchParameter] +$ShowStars, +# Set the use_transparancy of OBSFireworksShader +[Alias('use_transparancy')] +[ComponentModel.DefaultBindingProperty('use_transparancy')] +[Management.Automation.SwitchParameter] +$UseTransparancy, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'fireworks' +$ShaderNoun = 'OBSFireworksShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#ifndef OPENGL +#define mat2 float2x2 +#define fract frac +#define mix lerp +#endif + +uniform bool show_flash = true; +uniform bool show_stars = true; +uniform bool use_transparancy = true; + +float distLine(float2 p, float2 a, float2 b) { + float2 pa = p - a; + float2 ba = b - a; + float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * t); +} + +float linef(float2 uv, float2 a, float2 b, float w) { + //return smoothstep(w, w - 0.01, distLine(uv, a, b)); + return w / distLine(uv, a, b); +} + +float N21(float2 p) { + p = fract(p * float2(233.34, 851.73)); + p += dot(p, p + 23.45); + return fract(p.x * p.y); +} + +float2 N22(float2 p) { + float n = N21(p); + return float2(n, N21(p + n)); +} + +float N11(float n) { + return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); +} + +float particle(float2 uv, float2 p, float2 v, float r, float t) { + float g = -9.81; + float x = p.x + v.x * t; + float y = p.y + v.y * t + g / 2.0 * t * t; + float2 j = (float2(x, y) - uv) * 20.0; + float sparkle = 1.0 / dot(j, j); + return sparkle; +} + +float2 p1(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); +} + +float2 p2(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); +} + +float endTime(float h) { + return pow(h, 1.0 / 5.0) * 1.1; +} + +float explosion(float2 uv, float2 p, float s, float n, float f, float t) { + + float m = 0.0; + float dt = 0.5; + float seed2 = 0.32; + for(float i = 0.0; i < n; i++) { + seed2 += i; + float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); + float2 v = float2(cos(seed2), sin(seed2)) + rand; + m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); + } + return m; +} + +float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { + float2 p1v = p1(p, h, t); + float e = endTime(h); + return explosion(uv, p1v, s, n, f, t - e * 0.9); +} + +float shaft(float2 uv, float2 p, float w, float h, float t) { + float2 p1v = p1(p, h, t) + float2(0.0, 0.3); + float2 p2v = p2(p, h, t); + float e = 1.0 / 0.95 * endTime(h); + float2 j = (p1v - uv) * 15.0; + float sparkle = 1.0 / dot(j, j); + return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; +} + +float3 base(float2 uv) { + return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); +} + +float back(float2 uv, float2 p, float t) { + float dt = 0.3; + float j = length(p - uv); + float m = exp(-0.005 * j * j); + return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); +} + +float stars(float2 uv) { + float r = N21(uv); + return smoothstep(0.001, 0.0, r); +} + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv - float2(0.5,0.5); + uv.y = uv.y * -1; + float t = elapsed_time / 10.0; + float scale = 10.0; + uv *= scale; + // + float4 col = image.Sample(textureSampler, v_in.uv); + if(show_stars){ + float c = stars(uv); + if(use_transparancy){ + col += float4(c,c,c,c)*(1.0-col.a); + }else{ + col += float4(c,c,c,c);//*(1.0-orig_col.a); + } + + } + + float a = -0.035 * sin(t * 15.0); + float co = cos(a); + float si = sin(a); + mat2 trans1 = mat2(float2(co, si), float2(-si, co)); + float2 trans2 = float2(-15.0 * a, 0.0); +#ifndef OPENGL + uv = mul(uv, trans1); +#else + uv *= trans1; +#endif + uv += trans2; + + for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { + float ti = mod(t * 9.0 - i * 5.0, 4.0); + float scale = mix(2.0, 0.3, ti / 4.0); + float2 uvs = uv * scale; + float rand = N11(i); + float h = 10.0 + rand * 4.0; + float w = 0.02; + float n = 80.0; + float s = 0.9; + float f = 1.5; + float2 p = float2(mix(-8.0, 8.0, rand), -10.0); + float fw = fireworks(uvs, p, h, n, s, f, ti); + float3 bc = base(uv); + col += float4(bc*fw, fw); + col += shaft(uvs, p, w, h, ti); + if(show_flash){ + if(use_transparancy){ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; + }else{ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8); + } + } + } + + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFisheyeShader { + +[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] +param( +# Set the center_x_percent of OBSFisheyeShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Single] +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Single] +$CenterYPercent, +# Set the power of OBSFisheyeShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'fisheye' +$ShaderNoun = 'OBSFisheyeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float center_x_percent< + string label = "Center x percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power< + string label = "Power"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); + } else if(power <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); + } + return image.Sample(textureSampler, uv); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFisheyeXyShader { + +[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] +param( +# Set the center_x_percent of OBSFisheyeXyShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Single] +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeXyShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Single] +$CenterYPercent, +# Set the power_x of OBSFisheyeXyShader +[Alias('power_x')] +[ComponentModel.DefaultBindingProperty('power_x')] +[Single] +$PowerX, +# Set the power_y of OBSFisheyeXyShader +[Alias('power_y')] +[ComponentModel.DefaultBindingProperty('power_y')] +[Single] +$PowerY, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'fisheye-xy' +$ShaderNoun = 'OBSFisheyeXyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float center_x_percent< + string label = "Center x percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power_x< + string label = "Power x"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; +uniform float power_y< + string label = "Power y"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power_x >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; + } else if(power_x <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; + } + if (power_y >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; + } else if(power_y <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; + } + return image.Sample(textureSampler, uv); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFlipShader { + +[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] +param( +# Set the Horizontal of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the Vertical of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'Flip' +$ShaderNoun = 'OBSFlipShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// A Simple Flip Shader + +uniform bool Horizontal< + string label = "Flip horizontally"; +> = true; +uniform bool Vertical< + string label = "Flip vertically"; +> = true; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + if (Horizontal == true) { + pos.x = 1 - pos.x; + } + if (Vertical == true) { + pos.y = 1 - pos.y; + } + + return image.Sample(textureSampler, pos); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSFrostedGlassShader { + +[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] +param( +# Set the Alpha_Percent of OBSFrostedGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Amount of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Amount')] +[Single] +$Amount, +# Set the Scale of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Scale')] +[Single] +$Scale, +# Set the Animate of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the Horizontal_Border of OBSFrostedGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSFrostedGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSFrostedGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the notes of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'frosted_glass' +$ShaderNoun = 'OBSFrostedGlassShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter + +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Amount< + string label = "Amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.03; +uniform float Scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 5.1; +uniform bool Animate; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.1; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform string notes< + string widget_type = "info"; +> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; + +float rand(float2 co) +{ + float scale = Scale; + if (Animate) + scale *= rand_f; + float2 v1 = float2(92.0,80.0); + float2 v2 = float2(41.0,62.0); + return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + float3 tc = rgba.rgb * Border_Color.rgb; + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; + + if (uv_compare < (Border_Offset - 0.005)) + { + float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); + tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + tc = image.Sample(textureSampler, v_in.uv).rgb; + } + return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGammaCorrectionShader { + +[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] +param( +# Set the Red of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Red')] +[Single] +$Red, +# Set the Green of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Green')] +[Single] +$Green, +# Set the Blue of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the notes of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gamma_correction' +$ShaderNoun = 'OBSGammaCorrectionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter + +uniform float Red< + string label = "Red"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Green< + string label = "Green"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Blue< + string label = "Blue"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform string notes< + string widget_type = "info"; +> = "Modify Colors to correct for gamma, use equal values for general correction." + +float4 mainImage(VertData v_in) : TARGET +{ + float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); + float4 c = image.Sample(textureSampler, v_in.uv); + c.rgb = pow(c.rgb, 1.0 / gammaRGB); + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianBlurAdvancedShader { + +[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] +param( +# Set the Directions of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Directions')] +[Single] +$Directions, +# Set the Quality of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Quality')] +[Single] +$Quality, +# Set the Size of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Size')] +[Single] +$Size, +# Set the Mask_Left of OBSGaussianBlurAdvancedShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurAdvancedShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurAdvancedShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-blur-advanced' +$ShaderNoun = 'OBSGaussianBlurAdvancedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Directions< + string label = "Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) +uniform float Quality< + string label = "Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float Size< + string label = "Size (8.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 8.0; // BLUR SIZE (Radius) +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +float4 mainImage(VertData v_in) : TARGET +{ + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 + + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; + + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianBlurShader { + +[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] +param( +# Set the ViewProj of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the imageSize of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageSize')] +[Single[]] +$ImageSize, +# Set the imageTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageTexel')] +[Single[]] +$ImageTexel, +# Set the u_radius of OBSGaussianBlurShader +[Alias('u_radius')] +[ComponentModel.DefaultBindingProperty('u_radius')] +[Int32] +$URadius, +# Set the u_diameter of OBSGaussianBlurShader +[Alias('u_diameter')] +[ComponentModel.DefaultBindingProperty('u_diameter')] +[Int32] +$UDiameter, +# Set the u_texelDelta of OBSGaussianBlurShader +[Alias('u_texelDelta')] +[ComponentModel.DefaultBindingProperty('u_texelDelta')] +[Single[]] +$UTexelDelta, +# Set the elapsed_time of OBSGaussianBlurShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianBlurShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianBlurShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianBlurShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the kernel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernel')] +[String] +$Kernel, +# Set the kernelTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernelTexel')] +[Single[]] +$KernelTexel, +# Set the pixel_size of OBSGaussianBlurShader +[Alias('pixel_size')] +[ComponentModel.DefaultBindingProperty('pixel_size')] +[Single] +$PixelSize, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-blur' +$ShaderNoun = 'OBSGaussianBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +// OBS Default +uniform float4x4 ViewProj; + +// Settings (Shared) +uniform texture2d image; +uniform float2 imageSize; +uniform float2 imageTexel; +uniform int u_radius; +uniform int u_diameter; +uniform float2 u_texelDelta; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; + +// Settings (Private) +//uniform float registerkernel[25]; +uniform texture2d kernel; +uniform float2 kernelTexel; +uniform float pixel_size = 1.0; + +sampler_state pointClampSampler { + Filter = Point; + AddressU = Clamp; + AddressV = Clamp; +}; + +sampler_state bilinearClampSampler { + Filter = Bilinear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float Gaussian(float x, float o) +{ + float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); +} + +VertData VSDefault(VertData vert_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv; + return vert_out; +} + +float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel) + { + float l_gauss = Gaussian(0.0, 1.0); + float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_gauss += l_g; + } + l_value = l_value * (1.0 / l_gauss); + return l_value; +} + +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + } + return l_value; +} + +/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + if (pixel_size % 2 == 0) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = p_texel; + float2 l_koffset = p_kernelTexel; + for (int k = 1; k <= pixel_size; k++) { + float l_g = kernel.Sample(pointClampSampler, l_koffset).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_uvoffset += p_texel; + l_koffset += p_kernelTexel; + } + return l_value; + } else { + return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) + } +}*/ + +float4 PSGaussian(VertData vert_in) : TARGET +{ + + float4 color = image.Sample(pointClampSampler, vert_in.uv); + + float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + + return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); + + /* + return InternalGaussianPrecalculated( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ + + /* + return InternalGaussianPrecalculatedNVOptimize( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSGaussian(vert_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianBlurSimpleShader { + +[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] +param( +# Set the Strength of OBSGaussianBlurSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Int32] +$Strength, +# Set the Mask_Left of OBSGaussianBlurSimpleShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurSimpleShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurSimpleShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurSimpleShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-blur-simple' +$ShaderNoun = 'OBSGaussianBlurSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int Strength< + string label = "Strength (1)"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 1.0; +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +float4 mainImage(VertData v_in) : TARGET +{ + if(Strength <= 0) + return image.Sample(textureSampler, v_in.uv); + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 + + float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) + float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) + + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; + + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianExampleShader { + +[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] +param( +# Set the ViewProj of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianExampleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianExampleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianExampleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSGaussianExampleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSGaussianExampleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the initial_image of OBSGaussianExampleShader +[Alias('initial_image')] +[ComponentModel.DefaultBindingProperty('initial_image')] +[String] +$InitialImage, +# Set the before_image of OBSGaussianExampleShader +[Alias('before_image')] +[ComponentModel.DefaultBindingProperty('before_image')] +[String] +$BeforeImage, +# Set the after_image of OBSGaussianExampleShader +[Alias('after_image')] +[ComponentModel.DefaultBindingProperty('after_image')] +[String] +$AfterImage, +# Set the text_color of OBSGaussianExampleShader +[Alias('text_color')] +[ComponentModel.DefaultBindingProperty('text_color')] +[String] +$TextColor, +# Set the max_distance of OBSGaussianExampleShader +[Alias('max_distance')] +[ComponentModel.DefaultBindingProperty('max_distance')] +[Single] +$MaxDistance, +# Set the exp of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('exp')] +[Single] +$Exp, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-example' +$ShaderNoun = 'OBSGaussianExampleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; + +/*-------------------------. +| :: Texture and sampler:: | +''-------------------------*/ + + +uniform texture2d initial_image; +sampler_state initial_sampler +{ + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = initial_image; +}; + +uniform texture2d before_image; +sampler_state before_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = before_image; +}; + +uniform texture2d after_image; +sampler_state after_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = after_image; +}; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct ColorData { + float4 initial_color : SV_TARGET0; + float4 before_color: SV_TARGET1; + float4 after_color : SV_TARGET2; +}; + +uniform float4 text_color; +uniform float max_distance; +uniform float exp; + +#define PI 3.141592653589793238462643383279502884197169399375105820974 + +VertData mainTransform(VertData v_in) +{ + VertData vert_out = v_in; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} + +float4 grayscale(float4 color) +{ + float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; + return float4(grayscale, grayscale, grayscale, color.a); +} + +float4 gaussian(VertData v_in, float angle) +{ + float rad = radians(angle); + float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); + float2 dir_2 = dir * 2.0; + float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; + + float4 px_away = image.Sample(textureSampler, v_in.uv + dir); + px_away += image.Sample(textureSampler, v_in.uv - dir); + px_away *= 0.25; + + float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away *= 0.0625; + + return ret + px_away + px_2_away; +} + +ColorData setColorData(VertData v_in): SV_TARGET0 +{ + //string RenderTarget0 = "initial_image"; + ColorData cd;// = (ColorData)0; + cd.initial_color = image.Sample(textureSampler, v_in.uv); + cd.before_color = float4(0.0,0.0,1.0,1.0); + cd.after_color = float4(1.0,0.0,0.0,1.0); + return cd; +} + +float4 blurImageH(VertData v_in) : SV_TARGET1 +{ + //string RenderTarget1 = "before_image"; + //ColorData cd = (ColorData)0; + //cd.initial_color = image.Sample(textureSampler, v_in.uv); + //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); + return float4(0.0,0.0,1.0,1.0); +} + +float4 blurImageV(VertData v_in) : SV_TARGET2 +{ + //string RenderTarget2 = "after_image"; + //ColorData cd = (ColorData)0; + //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); + return float4(1.0,0.0,0.0,1.0); +} + +float4 mainImage(VertData v_in) : SV_TARGET0 +{ + float4 color; + ColorData cd;// = (ColorData)0; + + //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); + //cd.before_color = before_image.Sample(before_sampler, v_in.uv); + cd.after_color = after_image.Sample(before_sampler, v_in.uv); + + if (max_distance <= 5) { + color = cd.before_color; + } + else { + color = cd.after_color;//image.Sample(textureSampler, v_in.uv); + } + + float4 gray = grayscale(color); + float4 gray_text = grayscale(text_color); + float d = distance(gray.rgb, gray_text.rgb); + if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ + float d_c = pow(d*2, exp) / pow(2, exp); + d_c = sin(d_c * PI / 2); + d = pow(1.0 - sin(d * PI/4), exp); + + color.rgb = float3(d,d,d); + } + + return color; +} + +technique Draw +{ + pass pre + { + vertex_shader = mainTransform(v_in); + pixel_shader = setColorData(v_in); + } + + pass b0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageH(v_in); + } + + pass b1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageV(v_in); + } + + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } + +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianSimpleShader { + +[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] +param( +# Set the ViewProj of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianSimpleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianSimpleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianSimpleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianSimpleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSGaussianSimpleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSGaussianSimpleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSGaussianSimpleShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSGaussianSimpleShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSGaussianSimpleShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the samples of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the LOD of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('LOD')] +[Int32] +$LOD, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-simple' +$ShaderNoun = 'OBSGaussianSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/ltScRG Converted inspiration + +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float + +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** + +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ + +// 16x acceleration of https://www.shadertoy.com/view/4tSyzy +// by applying gaussian at intermediate MIPmap level. + +uniform int samples< + string label = "Samples"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 25; + int step = 1; +> = 16; +uniform int LOD< + string label = "LOD"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 2; // gaussian done on MIPmap at scale LOD + +float gaussian(vec2 i) +{ + float sigma = (float(samples) * .25); + return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); +} + +vec4 blur(vec2 U, vec2 scale) +{ + vec4 O = vec4(0,0,0,0); + int sLOD = (1 << LOD); // tile size = 2^LOD + int s = samples / sLOD; + + for (int i = 0; i < s * s; i++) + { + vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; + O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); + //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); + //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); + } + + return O / O.a; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; + //float2 iResolution = 1 - v_in.uv + 1.0; + //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); + return blur(v_in.uv / iResolution, 1.0 / iResolution); + //return rgba; +} + + + + + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGbCameraShader { + +[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] +param( +# Set the pixelSize of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('pixelSize')] +[Single] +$PixelSize, +# Set the dither_factor of OBSGbCameraShader +[Alias('dither_factor')] +[ComponentModel.DefaultBindingProperty('dither_factor')] +[Single] +$DitherFactor, +# Set the alternative_bayer of OBSGbCameraShader +[Alias('alternative_bayer')] +[ComponentModel.DefaultBindingProperty('alternative_bayer')] +[Management.Automation.SwitchParameter] +$AlternativeBayer, +# Set the brightness of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the contrast of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('contrast')] +[Single] +$Contrast, +# Set the gamma of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('gamma')] +[Single] +$Gamma, +# Set the color_1 of OBSGbCameraShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the color_2 of OBSGbCameraShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the color_3 of OBSGbCameraShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the color_4 of OBSGbCameraShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gb-camera' +$ShaderNoun = 'OBSGbCameraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +/* + * ------------------------------------------------------------ + * "THE BEERWARE LICENSE" (Revision 42): + * maple wrote this code. As long as you retain this + * notice, you can do whatever you want with this stuff. If we + * meet someday, and you think this stuff is worth it, you can + * buy me a beer in return. + * ------------------------------------------------------------ + * from https://www.shadertoy.com/view/3tSXRh + * adopted for OBS by Exeldro + * ------------------------------------------------------------ + */ + +uniform float pixelSize< + string label = "Pixel Size"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 50.0; + float step = 0.1; +> = 3.0; - # The sampling method. It will default to "lanczos". - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("sampling")] - [string] - $Sampling = 'lanczos', +uniform float dither_factor< + string label = "Dither Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; - # If set, will keep the aspect ratio when scaling. - # This is only valid if the sampling method is set to "lanczos". - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("undistort")] - [Alias('Undistort')] - [switch] - $KeepAspectRatio, +uniform bool alternative_bayer; - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' +uniform float brightness< + string label = "Brightness"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float contrast< + string label = "Contrast"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float gamma< + string label = "Gamma"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.6; +uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; +uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; +uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; +uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) +// quantize coords to low resolution +float2 pixelize(float2 uv, float2 pixelSize) { + float2 factor = pixelSize / uv_size; + return floor(uv / factor) * factor; +} + +float3 colorLUT(float3 color) { + float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; + if(gray < 0.25) + return color_1.rgb; + if(gray < 0.50) + return color_2.rgb; + if(gray < 0.75) + return color_3.rgb; + return color_4.rgb; +} + +// adjust brightness, contrast and gamma levels of a color +float3 levels(float3 color, float brightness, float contrast, float3 gamma) { + float3 value = (color - 0.5) * contrast + 0.5; + value = clamp(value + brightness, 0.0, 1.0); + return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); +} +float3 levels(float3 color, float brightness, float contrast, float gamma) { + return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); +} + +// applies the dithering filter to a color map +float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { + // reduces pixel space to the selected pixel size + float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); + + // applies the bayer matrix filter to the color map + pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); + int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); + float bayer; + if (alternative_bayer){ +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ + 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ + 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ + 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ + 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; + } else { +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 48, 12, 60, 3, 51, 15, 63, + 32, 16, 44, 28, 35, 19, 47, 31, + 8, 56, 4, 52, 11, 59, 7, 55, + 40, 24, 36, 20, 43, 27, 39, 23, + 2, 50, 14, 62, 1, 49, 13, 61, + 34, 18, 46, 30, 33, 17, 45, 29, + 10, 58, 6, 54, 9, 57, 5, 53, + 42, 26, 38, 22, 41, 25, 37, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; } - $DynamicParameters + float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); + // limits it to the selected palette + color = colorLUT(bayerColor); + + return color; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); + texcoord = clamp(texcoord, 0.001, 1.0); + float4 c = image.Sample(textureSampler, texcoord); + float3 color = c.rgb; + + color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); + + color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); + + return float4(color.r, color.g, color.b, c.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "Scale" + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "scale_filter" - filterSettings = [Ordered]@{resolution=$Resolution;sampling=$Sampling;undistort=$KeepAspectRatio -as [bool]} - NoResponse = $myParameters["NoResponse"] + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } +} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSScrollFilter { - - - param( - # The horizontal scroll speed. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("speed_x")] - [Alias('SpeedX', 'Speed_X','HSpeed')] - [double] - $HorizontalSpeed, - - # The vertical scroll speed. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("speed_y")] - [Alias('SpeedY', 'Speed_Y','VSpeed')] - [double] - $VerticalSpeed, - - # If set, will not loop - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [switch] - $NoLoop, - - # If provided, will limit the width. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-500, 500)] - [ComponentModel.DefaultBindingProperty("cx")] - [Alias('LimitX', 'Limit_CX','WidthLimit')] - [double] - $LimitWidth, - # If provided, will limit the height. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateRange(-500, 500)] - [ComponentModel.DefaultBindingProperty("cy")] - [Alias('LimitY', 'Limit_CY','HeightLimit')] - [double] - $LimitHeight, +} - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlassShader { +[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] +param( +# Set the Alpha_Percent of OBSGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Offset_Amount of OBSGlassShader +[Alias('Offset_Amount')] +[ComponentModel.DefaultBindingProperty('Offset_Amount')] +[Single] +$OffsetAmount, +# Set the xSize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('xSize')] +[Int32] +$XSize, +# Set the ySize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('ySize')] +[Int32] +$YSize, +# Set the Reflection_Offset of OBSGlassShader +[Alias('Reflection_Offset')] +[ComponentModel.DefaultBindingProperty('Reflection_Offset')] +[Int32] +$ReflectionOffset, +# Set the Horizontal_Border of OBSGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the Glass_Color of OBSGlassShader +[Alias('Glass_Color')] +[ComponentModel.DefaultBindingProperty('Glass_Color')] +[String] +$GlassColor, +# Set the notes of OBSGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "Scroll" - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +process { +$shaderName = 'glass' +$ShaderNoun = 'OBSGlassShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Offset_Amount< + string label = "Offset Amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; +uniform int xSize< + string label = "x Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int ySize< + string label = "y Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int Reflection_Offset< + string label = "Reflection Offset"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 2; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = -0.01; + float maximum = 1.01; + float step = 0.01; +> = 0.5; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform float4 Glass_Color; +uniform string notes< + string widget_type = "info"; +> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +float mod(float a, float b){ + float d = a / b; + return (d-floor(d))*b; +} - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + - if ($myParameterData["loop"]) { - $myParameterData["loop"] = -not $myParameterData["loop"] - } - if ($myParameterData["cx"]) { - $myParameterData["limit_cx"] = $true - } - if ($myParameterData["cy"]) { - $myParameterData["limit_cy"] = $true - } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "scroll_filter" - filterSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } + int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); + int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); + float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); + float2 uv = v_in.uv + offsets; + float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } + float4 rgba = image.Sample(textureSampler, v_in.uv); + float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); + rgba = image.Sample(textureSampler, uv); + float4 rgba_glass = image.Sample(textureSampler, uv2); + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 + if (uv_compare < (Border_Offset - 0.005)) + { + rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + rgba_output = image.Sample(textureSampler, v_in.uv); + } + return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); +} - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - } -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSShaderFilter { - - - [Alias('Add-OBSShaderFilter')] - param( - # The text of the shader - [Parameter(ValueFromPipelineByPropertyName)] - [string]$ShaderText, - - # The file path to the shader, or the short file name of the shader. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ShaderName')] - [string] - $ShaderFile, - - # Any other settings for the shader. - # To see what the name of a shader setting is, change it in the user interface and then get the input's filters. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ShaderSettings')] - [PSObject] - $ShaderSetting, + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "Shader" + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - $shaderSettings = [Ordered]@{} - if ($ShaderText) { - $shaderSettings.shader_text = $ShaderText + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - elseif ($ShaderFile) { - if ($ShaderFile -match '[\\/]') { - $shaderSettings.shader_file_name = "$($ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($ShaderFile))" -replace "\\", "/" - } else { - if (-not $script:CachedOBSShaderFilters) { - $script:CachedOBSShaderFilters = - Get-OBS | # Get the OBS object - Select-Object -ExpandProperty Process | # which has a process - Split-Path | Split-Path | Split-Path | # from it's parent path, we go up three levels. - Join-Path -ChildPath data | Join-Path -ChildPath obs-plugins | # then down into plugin data. - Get-ChildItem -Filter obs-shaderfilter | - Get-ChildItem -Filter examples | - Get-ChildItem -File # get all of the files in this directory + } +} - } +} - $foundShaderFile = $script:CachedOBSShaderFilters | - Where-Object Name -Like "$shaderFile*" | - Select-Object -First 1 - if ($foundShaderFile) { - $shaderSettings.shader_file_name = $foundShaderFile.FullName -replace "\\", "/" - } - } - } +} - if ($shaderSetting) { - if ($shaderSetting -is [Collections.IDictionary]) { - foreach ($kv in $shaderSetting.GetEnumerator()) { - $shaderSettings[$kv.Key] = $kv.Value - } - } elseif ($shaderSetting -is [psobject]) { - foreach ($prop in $shaderSetting.psobject.properties) { - $shaderSettings[$prop.Name] = $prop.Value - } - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlitchAnalogShader { - if ($shaderSettings.shader_file_name) { - $shaderSettings.from_file = $true - } - - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "shader_filter" - filterSettings = $shaderSettings - NoResponse = $myParameters["NoResponse"] - } +[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] +param( +# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader +[Alias('scan_line_jitter_displacement')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] +[Single] +$ScanLineJitterDisplacement, +# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader +[Alias('scan_line_jitter_threshold_percent')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] +[Int32] +$ScanLineJitterThresholdPercent, +# Set the vertical_jump_amount of OBSGlitchAnalogShader +[Alias('vertical_jump_amount')] +[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] +[Single] +$VerticalJumpAmount, +# Set the vertical_speed of OBSGlitchAnalogShader +[Alias('vertical_speed')] +[ComponentModel.DefaultBindingProperty('vertical_speed')] +[Single] +$VerticalSpeed, +# Set the horizontal_shake of OBSGlitchAnalogShader +[Alias('horizontal_shake')] +[ComponentModel.DefaultBindingProperty('horizontal_shake')] +[Single] +$HorizontalShake, +# Set the color_drift_amount of OBSGlitchAnalogShader +[Alias('color_drift_amount')] +[ComponentModel.DefaultBindingProperty('color_drift_amount')] +[Single] +$ColorDriftAmount, +# Set the color_drift_speed of OBSGlitchAnalogShader +[Alias('color_drift_speed')] +[ComponentModel.DefaultBindingProperty('color_drift_speed')] +[Single] +$ColorDriftSpeed, +# Set the pulse_speed_percent of OBSGlitchAnalogShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the alpha_percent of OBSGlitchAnalogShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the rotate_colors of OBSGlitchAnalogShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Replace_Image_Color of OBSGlitchAnalogShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGlitchAnalogShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSGlitchAnalogShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat - } - return - } - # Add the filter. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 +process { +$shaderName = 'glitch_analog' +$ShaderNoun = 'OBSGlitchAnalogShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float scan_line_jitter_displacement< + string label = "Scan line jitter"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.0; // (displacement, threshold) +uniform int scan_line_jitter_threshold_percent< + string label = "scan line jitter threshold percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 95; +uniform float vertical_jump_amount< + string label = "Vertical jump amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float vertical_speed< + string label = "Vertical speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform float horizontal_shake< + string label = "Horizontal shake"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_amount< + string label = "Color drift amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_speed< + string label = "Color drift speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int alpha_percent< + string label = "Aplha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> ="play with settings!"; - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - - } - - } +float nrand(float x, float y) +{ + float value = dot(float2(x, y), float2(12.9898 , 78.233 )); + return frac(sin(value) * 43758.5453); } +float4 mainImage(VertData v_in) : TARGET +{ + float speed = pulse_speed_percent * 0.01; + float alpha = alpha_percent * 0.01; + float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; + float u = v_in.uv.x; + float v = v_in.uv.y; + float t = sin(elapsed_time * speed) * 2 - 1; + float4 rgba = image.Sample(textureSampler, v_in.uv); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSharpnessFilter { - - - [Alias('Add-OBSSharpnessFilter')] - param( - # The Sharpness. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("sharpness")] - [ValidateRange(0,1)] - [double] - $Sharpness, + // Scan line jitter + float jitter = nrand(v, t) * 2 - 1; + jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; - # If set, will remove a filter if one already exists. - # If this is not provided and the filter already exists, the settings of the filter will be changed. - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSSourceFilter) { - $script:AddOBSSourceFilter = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSSourceFilter','Function') - $script:AddOBSSourceFilter - } else { - $script:AddOBSSourceFilter - } - $IncludeParameter = @() - $ExcludeParameter = 'FilterKind','FilterSettings' + // Vertical jump + float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); + + // Horizontal shake + float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; + //// Color drift + float drift = sin(jump + color_drift_speed) * color_drift_amount; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); + float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); + + if(rotate_colors) + { + // get general time number between 0 and 4 + float tx = (t + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls + //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 + src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); + //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 + src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); + //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 + src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); + + } + + float4 color = rgba; + float4 original_color = color; + rgba = float4(src1.x, src2.x, src1.y, alpha); + + if (Apply_To_Alpha_Layer) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color, alpha); + } + + if (Apply_To_Specific_Color) + { + color = original_color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + + return rgba; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["FilterName"]) { - $filterName = $myParameters["FilterName"] = "Sharpness" + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } - - $addSplat = @{ - filterName = $myParameters["FilterName"] - SourceName = $myParameters["SourceName"] - filterKind = "Sharpness_filter" - filterSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName } - if ($MyParameters["PassThru"]) { - $addSplat.Passthru = $MyParameters["PassThru"] - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSSourceFilter @addSplat - } else { - $addSplat.Remove('FilterKind') - Set-OBSSourceFilterSettings @addSplat + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - return } - # Add the input. - $outputAddedResult = Add-OBSSourceFilter @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSSourceFilter -FilterName $addSplat.FilterName -SourceName $addSplat.SourceName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the existing filter. - $existingFilter = Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - # then apply the settings - $existingFilter.Set($addSplat.filterSettings) - # and output them - $existingFilter - # (don't forget to null the result, so we don't show this error) - $outputAddedResult = $null - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # Otherwise, get the input from the filters. - Get-OBSSourceFilter -SourceName $addSplat.SourceName -FilterName $addSplat.FilterName - + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } +} + + +} #.ExternalHelp obs-powershell-Help.xml -function Add-OBSInput { - +function Get-OBSGlitchShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] -[Alias('obs.powershell.websocket.CreateInput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, - +# Set the AMT of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('AMT')] +[Single] +$AMT, +# Set the SPEED of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('SPEED')] +[Single] +$SPEED, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'glitch' +$ShaderNoun = 'OBSGlitchShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/MtXBDs +//inputs +uniform float AMT< + string label = "AMT"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.2; //0 - 1 glitch amount +uniform float SPEED< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; //0 - 1 speed +//2D (returns 0 - 1) +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float randomRange (in float2 seed, in float min, in float max) { + return min + random2d(seed) * (max - min); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// return 1 if v inside 1d range +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + +float4 mainImage(VertData v_in) : TARGET +{ + + float time = floor(elapsed_time * SPEED * 60.0); + float2 uv = v_in.uv; + + //copy orig + float4 outCol = image.Sample(textureSampler, uv); + + //randomly offset slices horizontally + float maxOffset = AMT/2.0; + for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { + float sliceY = random2d(float2(time , 2345.0 + float(i))); + float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; + float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); + float2 uvOff = uv; + uvOff.x += hOffset; + if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ + outCol = image.Sample(textureSampler, uvOff); + } + } + + //do slight offset on one entire channel + float maxColOffset = AMT/6.0; + float rnd = random2d(float2(time , 9545.0)); + float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), + randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); + if (rnd < 0.33){ + outCol.r = image.Sample(textureSampler, uv + colOffset).r; + + }else if (rnd < 0.66){ + outCol.g = image.Sample(textureSampler, uv + colOffset).g; + + } else{ + outCol.b = image.Sample(textureSampler, uv + colOffset).b; + } + + return outCol; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSProfile { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] -[Alias('obs.powershell.websocket.CreateProfile')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -45339,213 +39232,260 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSScene { - +function Get-OBSGlowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] -[Alias('obs.powershell.websocket.CreateScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the glow_percent of OBSGlowShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSGlowShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSGlowShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSGlowShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed of OBSGlowShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGlowShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the notes of OBSGlowShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'glow' +$ShaderNoun = 'OBSGlowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int glow_percent< + string label = "Glow percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed< + string label = "Pulse speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform string notes< + string widget_type = "info"; +> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; } +float BlurStyler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneCollection { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] -[Alias('obs.powershell.websocket.CreateSceneCollection')] -param( +float4 mainImage(VertData v_in) : TARGET +{ + float2 offsets[4]; + offsets[0] = float2(-0.1, 0.125); + offsets[1] = float2(-0.1, -0.125); + offsets[2] = float2(0.1, -0.125); + offsets[3] = float2(0.1, 0.125); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // convert input for vector math + float4 col = image.Sample(textureSampler, v_in.uv); + float blur_amount = float(blur) /100.0; + float glow_amount = float(glow_percent) * 0.01; + float speed = float(pulse_speed) * 0.01; + float luminance_floor = float(min_brightness) /100.0; + float luminance_ceiling = float(max_brightness) /100.0; + if (col.a > 0.0) + { + //circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration -process { + // simple glow calc + for (int n = 0; n < 4; n++) { + b = BlurStyler(t, 0, c, d, ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + col += (ncolor * (glow_amount * b)); + } + } + } + return col; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -45554,127 +39494,418 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneItem { - +function Get-OBSGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] -[Alias('obs.powershell.websocket.CreateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -[Alias('Add-OBSSceneSource')] +[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - +# Set the start_color of OBSGradientShader +[Alias('start_color')] +[ComponentModel.DefaultBindingProperty('start_color')] +[String] +$StartColor, +# Set the start_step of OBSGradientShader +[Alias('start_step')] +[ComponentModel.DefaultBindingProperty('start_step')] +[Single] +$StartStep, +# Set the middle_color of OBSGradientShader +[Alias('middle_color')] +[ComponentModel.DefaultBindingProperty('middle_color')] +[String] +$MiddleColor, +# Set the middle_step of OBSGradientShader +[Alias('middle_step')] +[ComponentModel.DefaultBindingProperty('middle_step')] +[Single] +$MiddleStep, +# Set the end_color of OBSGradientShader +[Alias('end_color')] +[ComponentModel.DefaultBindingProperty('end_color')] +[String] +$EndColor, +# Set the end_step of OBSGradientShader +[Alias('end_step')] +[ComponentModel.DefaultBindingProperty('end_step')] +[Single] +$EndStep, +# Set the alpha_percent of OBSGradientShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the pulse_speed of OBSGradientShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGradientShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the rotate_colors of OBSGradientShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the horizontal of OBSGradientShader +[ComponentModel.DefaultBindingProperty('horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the vertical of OBSGradientShader +[ComponentModel.DefaultBindingProperty('vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the gradient_center_width_percentage of OBSGradientShader +[Alias('gradient_center_width_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] +[Int32] +$GradientCenterWidthPercentage, +# Set the gradient_center_height_percentage of OBSGradientShader +[Alias('gradient_center_height_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] +[Int32] +$GradientCenterHeightPercentage, +# Set the notes of OBSGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gradient' +$ShaderNoun = 'OBSGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; +uniform float start_step< + string label = "Start step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.15; +uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; +uniform float middle_step< + string label = "Middle step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.4; +uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; +uniform float end_step< + string label = "End step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform int alpha_percent< + string label = "Alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int pulse_speed< + string label = "Pulse speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = true; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform bool horizontal; +uniform bool vertical; +uniform int gradient_center_width_percentage< + string label = "gradient center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int gradient_center_height_percentage< + string label = "gradient center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; +float EaseInOutCircTimer(float t, float b, float c, float d) { + t /= d / 2; + if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float BlurStyler(float t, float b, float c, float d, bool ease) +{ + if (ease) return EaseInOutCircTimer(t, 0, c, d); + return t; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +struct gradient +{ + float4 color; + float step; +}; + + +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.14159265f;//acos(-1); + float speed = float(pulse_speed) * 0.01; + float alpha = float(alpha_percent) * 0.01; + + //circular easing variable + float t = sin(elapsed_time * speed) * 2 - 1; + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + + float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); + float4 color = image.Sample(textureSampler, v_in.uv); + float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; + float4 gray = float4(luminance,luminance,luminance, 1); + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + b = BlurStyler(t, 0, c, d, ease); + + const int no_colors = 3; + float4 s_color = start_color; + float4 m_color = middle_color; + float4 e_color = end_color; + + if (rotate_colors) + { + // get general time number between 0 and 4 + float tx = (b + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls + + if (tx <= 2.0) + { + s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + } + + if ((tx >= 1.0) && (tx <= 3.0)) + { + s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + } + + if (tx >= 2.0) + { + s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + } + + if (tx < 0) + { + s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + } + } + + float4 colors[no_colors]; + colors[0] =s_color; + colors[1] = m_color; + colors[2] = e_color; + float step[no_colors]; + step[0] = start_step; + step[1] = middle_step; + step[2] = end_step; + + float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); + float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); + float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); + + float dist = distance(v_in.uv, gradient_center); + if (horizontal && (vertical == false)) + { + dist = distance(v_in.uv.y, gradient_center.y); + } + if (vertical && (horizontal == false)) + { + dist = distance(v_in.uv.x, gradient_center.x); + } + + float4 col = colors[0]; + for (int i = 1; i < no_colors; ++i) { + col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); + } + col.a = clamp(alpha, 0.0, 1.0); + if (Apply_To_Alpha_Layer == false) + color.a = alpha; + if (Apply_To_Specific_Color) + { + col.a = alpha; + float4 original_color = image.Sample(textureSampler, v_in.uv); + col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; } + // result = float4(redness, greenness,blueness,1); + //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); + //color.rgb += col * alpha; + //color.a += clamp(1.0 - alpha, 0.0,1.0); + ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); + //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); + color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + } + return color; + + +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -45683,125 +39914,191 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSourceFilter { - +function Get-OBSHalftoneShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] -[Alias('obs.powershell.websocket.CreateSourceFilter')] +[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] param( - +# Set the threshold of OBSHalftoneShader +[ComponentModel.DefaultBindingProperty('threshold')] +[Single] +$Threshold, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'halftone' +$ShaderNoun = 'OBSHalftoneShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.1415926535897932384626433832795 +#define PI180 float(PI / 180.0) +uniform float threshold< + string label = "Threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.6; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float sind(float a) +{ + return sin(a * PI180); +} + +float cosd(float a) +{ + return cos(a * PI180); +} + +float added(float2 sh, float sa, float ca, float2 c, float d) +{ + return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); +} + +float4 mainImage(VertData v_in) : TARGET +{ + // Halftone dot matrix shader + // @author Tomek Augustyn 2010 + + // Ported from my old PixelBender experiment + // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk + + float coordX = v_in.uv.x; + float coordY = v_in.uv.y; + float2 dstCoord = float2(coordX, coordY); + float2 rotationCenter = float2(0.5, 0.5); + float2 shift = dstCoord - rotationCenter; + + float dotSize = 3.0; + float angle = 45.0; + + float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); + float4 srcPixel = image.Sample(textureSampler, dstCoord); + + float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; + float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); + + // uncomment to see how the raster pattern looks + // gray = rasterPattern; + + return float4(gray, gray, gray, 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -45810,127 +40107,173 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Copy-OBSSceneItem { - +function Get-OBSHardBlinkShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] -[Alias('obs.powershell.websocket.DuplicateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneName')] -[string] -$DestinationSceneName, - +# Set the timeon of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeon')] +[Single] +$Timeon, +# Set the timeoff of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeoff')] +[Single] +$Timeoff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] -[string] -$DestinationSceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hard_blink' +$ShaderNoun = 'OBSHardBlinkShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// hard_blink shader created by https://github.com/WhazzItToYa +// +// Periodically makes the source image 100% transparent, in configurable intervals. +uniform float timeon< + string label = "Time On"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float timeoff< + string label = "Time Off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float m = timeon + timeoff; + float t = elapsed_time % m; + if (t < timeon) { + return color; + } else { + return float4(color.r, color.g, color.b, 0.0); + } +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -45939,101 +40282,212 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentPreviewScene { - +function Get-OBSHeatWaveSimpleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Rate of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Rate')] +[Single] +$Rate, +# Set the Strength of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Single] +$Strength, +# Set the Distortion of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Distortion')] +[Single] +$Distortion, +# Set the Opacity of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'heat-wave-simple' +$ShaderNoun = 'OBSHeatWaveSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Heat Wave Simple, Version 0.03, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// +// Description: +// Generate a crude pseudo heat wave displacement on an image source. +// +// Based on: https://www.shadertoy.com/view/td3GRn by Dombass +// +// Changelog: +// 0.03 - Added Opacity control +// 0.02 - Added crude Rate, Strength, and Distortion controls +// 0.01 - Initial release +uniform float Rate< + string label = "Rate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 5.0; +uniform float Strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = -25.0; + float maximum = 25.0; + float step = 0.01; +> = 1.0; +uniform float Distortion< + string label = "Distortion"; + string widget_type = "slider"; + float minimum = 3.0; + float maximum = 20.0; + float step = 0.01; +> = 10.0; +uniform float Opacity< + string label = "Opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.00; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float distort = clamp(Distortion, 3.0, 20.0); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + // Time varying pixel color + float jacked_time = Rate*elapsed_time; + float2 scale = float2(0.5, 0.5); + float str = clamp(Strength, -25.0, 25.0) * 0.01; + + uv += str * sin(scale*jacked_time + length( uv ) * distort); + float4 c = image.Sample( textureSampler, uv); + c.a *= saturate(Opacity*0.01); + return c; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -46042,101 +40496,321 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentProgramScene { - +function Get-OBSHexagonShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] -[Alias('obs.powershell.websocket.GetCurrentProgramScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Hex_Color of OBSHexagonShader +[Alias('Hex_Color')] +[ComponentModel.DefaultBindingProperty('Hex_Color')] +[String] +$HexColor, +# Set the Alpha_Percent of OBSHexagonShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Int32] +$AlphaPercent, +# Set the Quantity of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Quantity')] +[Single] +$Quantity, +# Set the Border_Width of OBSHexagonShader +[Alias('Border_Width')] +[ComponentModel.DefaultBindingProperty('Border_Width')] +[Int32] +$BorderWidth, +# Set the Blend of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Blend')] +[Management.Automation.SwitchParameter] +$Blend, +# Set the Equilateral of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Equilateral')] +[Management.Automation.SwitchParameter] +$Equilateral, +# Set the Zoom_Animate of OBSHexagonShader +[Alias('Zoom_Animate')] +[ComponentModel.DefaultBindingProperty('Zoom_Animate')] +[Management.Automation.SwitchParameter] +$ZoomAnimate, +# Set the Speed_Percent of OBSHexagonShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Int32] +$SpeedPercent, +# Set the Glitch of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Distort_X of OBSHexagonShader +[Alias('Distort_X')] +[ComponentModel.DefaultBindingProperty('Distort_X')] +[Single] +$DistortX, +# Set the Distort_Y of OBSHexagonShader +[Alias('Distort_Y')] +[ComponentModel.DefaultBindingProperty('Distort_Y')] +[Single] +$DistortY, +# Set the Offset_X of OBSHexagonShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSHexagonShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the notes of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hexagon' +$ShaderNoun = 'OBSHexagonShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 Hex_Color; +uniform int Alpha_Percent< + string label = "Alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform float Quantity< + string label = "Quantity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 25; +uniform int Border_Width< + string label = "Border Width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 115; + int step = 1; +> = 15; // <- -15 to 85, -15 off top +uniform bool Blend; +uniform bool Equilateral; +uniform bool Zoom_Animate; +uniform int Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool Glitch; +uniform float Distort_X< + string label = "Distort X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Distort_Y< + string label = "Distort Y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Offset_Y< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform string notes< + string widget_type = "info"; +>= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; +float mod(float x, float y) +{ + return x - y * floor(x/y); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x/y); +} + +// 0 on edges, 1 in non_edge +float hex(float2 p) { + float xyratio = 1; + if (Equilateral) + xyratio = uv_size.x /uv_size.y; + + // calc p + p.x = mul(p.x,xyratio); + p.y += mod(floor(p.x) , 2.0)*0.5; + p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); + return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); + float alpha = float(Alpha_Percent) * 0.01; + float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); + float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; + float speed = float(Speed_Percent) * 0.01; + float time = (1 + sin(elapsed_time * speed))*0.5; + if (Zoom_Animate) + quantity *= time; + + // create a (pos)ition reference, hex radius and smoothstep out the non_edge + float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); + if (Glitch) + quantity *= lerp(pos.x, pos.y, rand_f); + float2 p = (pos * quantity); // number of hexes to be created + float r = (1.0 -0.7)*0.5; // cell default radius + float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge + + // make the border colorable - non_edge is scaled + float4 c = float4(non_edge, non_edge,non_edge,1.0) ; + if (non_edge < 1) + { + c = Hex_Color; + c.a = alpha; + if (Blend) + c = lerp(rgba, c, 1 - non_edge); + return lerp(rgba,c,alpha); + } + return lerp(rgba, c * rgba, alpha); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -46145,307 +40819,316 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransition { - +function Get-OBSHslHsvSaturationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the hslSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] +[Single] +$HslSaturationFactor, +# Set the hslGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslGamma')] +[Single] +$HslGamma, +# Set the hsvSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] +[Single] +$HsvSaturationFactor, +# Set the hsvGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvGamma')] +[Single] +$HsvGamma, +# Set the adjustmentOrder of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('adjustmentOrder')] +[Int32] +$AdjustmentOrder, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hsl_hsv_saturation' +$ShaderNoun = 'OBSHslHsvSaturationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float hslSaturationFactor< + string label = "HSL Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float hslGamma< + string label = "HSL Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float hsvSaturationFactor< + string label = "HSV Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float hsvGamma< + string label = "HSV Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform int adjustmentOrder< + string label = "Order"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; + int option_1_value = 2; + string option_1_label = "HSL first, then HSV"; + int option_2_value = 3; + string option_2_label = "HSV first, then HSL"; +> = 1; + +// HSV conversion +float3 rgb2hsv(float3 c) { + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } +float3 hsv2rgb(float3 c) { + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} -} +// HSL conversion - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransitionCursor { +float3 rgb2hsl(float3 c) { + float maxVal = max(c.r, max(c.g, c.b)); + float minVal = min(c.r, min(c.g, c.b)); + float delta = maxVal - minVal; + float h = 0.0; + float s = 0.0; + float l = (maxVal + minVal) / 2.0; + if(delta != 0) { + if(l < 0.5) s = delta / (maxVal + minVal); + else s = delta / (2.0 - maxVal - minVal); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); + else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; + else h = (c.r - c.g) / delta + 4.0; + h /= 6.0; + } -process { + return float3(h, s, l); +} +float hue2rgb(float p, float q, float t) { + if(t < 0.0) t += 1.0; + if(t > 1.0) t -= 1.0; + if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if(t < 1.0/2.0) return q; + if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 hsl2rgb(float3 c) { + float r, g, b; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + if(c.y == 0.0) { + r = g = b = c.z; + } else { + float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; + float p = 2.0 * c.z - q; + r = hue2rgb(p, q, c.x + 1.0/3.0); + g = hue2rgb(p, q, c.x); + b = hue2rgb(p, q, c.x - 1.0/3.0); + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + return float3(r, g, b); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +float3 adjustColorWithOrder(float3 originalColor) { + if (adjustmentOrder == 1) { + // Parallel adjustment (both HSL and HSV operate on the original image and then blend) + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 hslAdjustedColor = hsl2rgb(hslAdjusted); + + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); + + float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; + return finalColor; + } + else if (adjustmentOrder == 2) { + // HSL first, then HSV + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 afterHSL = hsl2rgb(hslAdjusted); + float3 hsvAdjusted = rgb2hsv(afterHSL); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + return hsv2rgb(hsvAdjusted); + } + else if (adjustmentOrder == 3) { + // HSV first, then HSL + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 afterHSV = hsv2rgb(hsvAdjusted); + float3 hslAdjusted = rgb2hsl(afterHSV); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + return hsl2rgb(hslAdjusted); + } + return originalColor; // Default to original color in case of unexpected values +} + +// Final composite + +float4 mainImage(VertData v_in) : TARGET +{ + float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; + float3 adjustedColor = adjustColorWithOrder(originalColor); + return float4(adjustedColor, 1.0); // preserving the original alpha +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroup { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] -[Alias('obs.powershell.websocket.GetGroupList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -46454,214 +41137,235 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroupSceneItem { - +function Get-OBSHueRotatonShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] -[Alias('obs.powershell.websocket.GetGroupSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Speed of OBSHueRotatonShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Hue_Override of OBSHueRotatonShader +[Alias('Hue_Override')] +[ComponentModel.DefaultBindingProperty('Hue_Override')] +[Management.Automation.SwitchParameter] +$HueOverride, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hue-rotaton' +$ShaderNoun = 'OBSHueRotatonShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Hue Rotation shader, version 1.0 for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Description: +// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue +// override option is provided to force a specific rotating hue instead of the original image''s hue. +// +// Changelog: +// 1.0 - Initial release +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 10.00; +uniform bool Hue_Override = false; +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); } - -} - +#define Epsilon 1e-10 -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSHotkey { +float3 RGBtoHCV(in float3 RGB) +{ + // Based on work by Sam Hocevar and Emil Persson + float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); + float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); + float C = Q.x - min(Q.w, Q.y); + float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); + return float3(H, C, Q.x); +} +float3 HSVtoRGB(in float3 HSV) +{ + float3 RGB = HUEtoRGB(HSV.x); + return ((RGB - 1) * HSV.y + 1) * HSV.z; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] -[Alias('obs.powershell.websocket.GetHotkeyList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float3 RGBtoHSV(in float3 RGB) +{ + float3 HCV = RGBtoHCV(RGB); + float S = HCV.y / (HCV.z + Epsilon); + return float3(HCV.x, S, HCV.z); +} +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float4 col_in = image.Sample(textureSampler, uv); + float3 col_out; + float3 HSV = RGBtoHSV(col_in.rgb); -process { + if(Hue_Override) + HSV.x = elapsed_time * Speed * 0.01; + else + HSV.x += elapsed_time * Speed * 0.01; + // Normalize Hue + HSV.x = frac(HSV.x); + + col_out = HSVtoRGB(HSV); + return float4(col_out, col_in.a); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -46670,106 +41374,192 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInput { - +function Get-OBSIntensityScopeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] -[Alias('obs.powershell.websocket.GetInputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] param( - +# Set the gain of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('gain')] +[Single] +$Gain, +# Set the blend of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('blend')] +[Single] +$Blend, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'intensity-scope' +$ShaderNoun = 'OBSIntensityScopeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Robin Green, Dec 2016 +// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro +uniform float gain< + string label = "Gain"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 1.00; + float step = 0.01; +> = 0.3; +uniform float blend< + string label = "Blend"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.6; +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv.y = 1.0 - uv.y; + + // calculate the intensity bucket for this pixel based on column height (padded at the top) + const float max_value = 270.0; + const float buckets = 512.0; + float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); + float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); + + // count the count the r,g,b and luma in this column that match the bucket + float4 count = float4(0.0, 0.0, 0.0, 0.0); + for( int i=0; i < 512; ++i ) { + float j = float(i) / buckets; + float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; + + // calculate the Rec.709 luma for this pixel + pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 logpixel = log(pixel); + if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; + if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; + if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; + if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; + } + + // sum luma into RGB, tweak log intensity for readability + count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; + + // output + return count; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -46778,224 +41568,266 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioBalance { - +function Get-OBSInvertLumaShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] -[Alias('obs.powershell.websocket.GetInputAudioBalance')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Invert_Color of OBSInvertLumaShader +[Alias('Invert_Color')] +[ComponentModel.DefaultBindingProperty('Invert_Color')] +[Management.Automation.SwitchParameter] +$InvertColor, +# Set the Invert_Luma of OBSInvertLumaShader +[Alias('Invert_Luma')] +[ComponentModel.DefaultBindingProperty('Invert_Luma')] +[Management.Automation.SwitchParameter] +$InvertLuma, +# Set the Gamma_Correction of OBSInvertLumaShader +[Alias('Gamma_Correction')] +[ComponentModel.DefaultBindingProperty('Gamma_Correction')] +[Management.Automation.SwitchParameter] +$GammaCorrection, +# Set the Test_Ramp of OBSInvertLumaShader +[Alias('Test_Ramp')] +[ComponentModel.DefaultBindingProperty('Test_Ramp')] +[Management.Automation.SwitchParameter] +$TestRamp, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'invert-luma' +$ShaderNoun = 'OBSInvertLumaShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Invert shader 1.0 - for OBS Shaderfilter +// Copyright 2021 by SkeletonBow +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv +// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool Invert_Color = false; +uniform bool Invert_Luma = true; +uniform bool Gamma_Correction = true; +uniform bool Test_Ramp = false; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float3 encodeSRGB(float3 linearRGB) +{ + float3 a = float3(12.92,12.92,12.92) * linearRGB; + float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); + float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); + return float3(lerp(a, b, c)); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float3 decodeSRGB(float3 screenRGB) +{ + float3 a = screenRGB / float3(12.92,12.92,12.92); + float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); + float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); + return float3(lerp(a, b, c)); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); +} + +float3 RGBtoYUV(float3 color) +{ + // YUV matriz (BT709 luma coefficients) +#ifdef OPENGL + float3x3 toYUV = float3x3( + float3(0.2126, -0.09991, 0.615), + float3(0.7152, -0.33609, -0.55861), + float3(0.0722, 0.436, -0.05639)); +#else + float3x3 toYUV = { + { 0.2126, -0.09991, 0.615 }, + { 0.7152, -0.33609, -0.55861 }, + { 0.0722, 0.436, -0.05639 }, + }; +#endif + return mul(color, toYUV); +} + +float3 YUVtoRGB(float3 color) +{ + // YUV matriz (BT709) +#ifdef OPENGL + float3x3 fromYUV = float3x3( + float3(1.000, 1.000, 1.000), + float3(0.0, -0.21482, 2.12798), + float3(1.28033, -0.38059, 0.0)); +#else + float3x3 fromYUV = { + { 1.000, 1.000, 1.000 }, + { 0.0, -0.21482, 2.12798 }, + { 1.28033, -0.38059, 0.0 }, + }; +#endif + return mul(color, fromYUV); +} + +float3 generate_ramps(float3 color, float2 uv) +{ + float3 ramp = float3(0.0, 0.0, 0.0); + if(uv.y < 0.2) + ramp.r = uv.x; // Red ramp + else if(uv.y < 0.4) + ramp.g = uv.x; // Green ramp + else if(uv.y < 0.6) + ramp.b = uv.x; // Blue ramp + else if(uv.y < 0.8) + ramp = float3(uv.x, uv.x, uv.x); // Grey ramp + else + ramp = HUEtoRGB(uv.x); // Hue rainbow + + return ramp; +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float4 obstex = image.Sample( textureSampler, uv ); + float3 color = obstex.rgb; + // Apply sRGB gamma transfer encode + if(Gamma_Correction) color = encodeSRGB( color ); + // Override display with test patterns to visually see what is happening + if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); + // RGB color invert + if( Invert_Color ) { + color = float3(1.0, 1.0, 1.0) - color; + } + // YUV luma invert + if( Invert_Luma ) { + float3 yuv = RGBtoYUV( color ); + yuv.x = 1.0 - yuv.x; + color = YUVtoRGB(yuv); + } + // Apply sRGB gamma transfer decode + if(Gamma_Correction) color = decodeSRGB( color ); + return float4(color, obstex.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioMonitorType { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47004,224 +41836,240 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioSyncOffset { - +function Get-OBSLuminance2Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the color of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the lumaMax of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMax')] +[Single] +$LumaMax, +# Set the lumaMin of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMaxSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] +[Single] +$LumaMaxSmooth, +# Set the lumaMinSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the invertImageColor of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'luminance2' +$ShaderNoun = 'OBSLuminance2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 color; +uniform float lumaMax< + string label = "Luma Max"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.05; +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMaxSmooth< + string label = "Luma Max Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.10; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; +uniform string notes< + string widget_type = "info"; +> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); + } + float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + //intensity = min(max(intensity,minIntensity),maxIntensity); + float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); + float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } + float amask = clo * chi; + + if (invertAlphaChannel) + { + amask = 1.0 - amask; + } + rgba *= color; + rgba.a = clamp(amask, 0.0, 1.0); - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + } + return rgba; +} - if ($PassThru) { - [PSCustomObject]$requestPayload +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioTracks { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] -[Alias('obs.powershell.websocket.GetInputAudioTracks')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47230,106 +42078,298 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputDefaultSettings { - +function Get-OBSLuminanceAlphaShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] -[Alias('obs.powershell.websocket.GetInputDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSLuminanceAlphaShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSLuminanceAlphaShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSLuminanceAlphaShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSLuminanceAlphaShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSLuminanceAlphaShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSLuminanceAlphaShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the color_matrix of OBSLuminanceAlphaShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the mul_val of OBSLuminanceAlphaShader +[Alias('mul_val')] +[ComponentModel.DefaultBindingProperty('mul_val')] +[Single] +$MulVal, +# Set the add_val of OBSLuminanceAlphaShader +[Alias('add_val')] +[ComponentModel.DefaultBindingProperty('add_val')] +[Single] +$AddVal, +# Set the level of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('level')] +[Single] +$Level, +# Set the invertAlphaChannel of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'luminance_alpha' +$ShaderNoun = 'OBSLuminanceAlphaShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float4x4 color_matrix; +uniform float4 color; +uniform float mul_val< + string label = "Mulitply"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float add_val< + string label = "Add"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 0.0; +uniform float level< + string label = "Level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> =1.0; +uniform bool invertAlphaChannel; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; + +VertDataOut mainTransform(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); + vert_out.uv = v_in.uv * mul_val + add_val; + vert_out.uv2 = v_in.uv ; + return vert_out; +} + +/*float3 GetLuminance(float4 rgba) +{ + float red = rbga.r; + float green = rgba.g; + float blue = rgba.b; + return (.299 * red) + (.587 * green) + (.114 * blue); +}*/ + +float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv) ; + if (rgba.a > 0.0) + { + + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + if (invertAlphaChannel) + { + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + + } + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = PSAlphaMaskRGBA(v_in); + } +} + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47338,219 +42378,205 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputKind { - +function Get-OBSLuminanceShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] -[Alias('obs.powershell.websocket.GetInputKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] param( - +# Set the color of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the level of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('level')] +[Single] +$Level, +# Set the invertImageColor of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('unversioned')] -[switch] -$Unversioned, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputMute { +process { +$shaderName = 'Luminance' +$ShaderNoun = 'OBSLuminanceShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 22, 2022 +uniform float4 color; +uniform float level< + string label = "Level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; +uniform string notes< + string widget_type = "info"; +> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] -[Alias('obs.powershell.websocket.GetInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float4 mainImage(VertData v_in) : TARGET +{ -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); + } + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + //intensity = min(max(intensity,minIntensity),maxIntensity); -process { + if (invertAlphaChannel) + { + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47559,229 +42585,439 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputPropertiesListPropertyItems { - +function Get-OBSMatrixShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] -[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the ViewProj of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSMatrixShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSMatrixShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSMatrixShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSMatrixShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSMatrixShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSMatrixShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSMatrixShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSMatrixShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSMatrixShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the mouse of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('mouse')] +[Single[]] +$Mouse, +# Set the Invert_Direction of OBSMatrixShader +[Alias('Invert_Direction')] +[ComponentModel.DefaultBindingProperty('Invert_Direction')] +[Management.Automation.SwitchParameter] +$InvertDirection, +# Set the lumaMin of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Ratio of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('Ratio')] +[Single] +$Ratio, +# Set the Alpha_Percentage of OBSMatrixShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSMatrixShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'matrix' +$ShaderNoun = 'OBSMatrixShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float4x4 ViewProj; +uniform texture2d image; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float2 mouse< + string label = "Virtual Mouse Coordinates"; + string widget_type = "slider"; + float2 minimum = {0, 0}; + float2 maximum = {100., 100.}; + float2 scale = {.01, .01}; + float2 step = {.01, .01}; +> = {0., 0.}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +int2 iMouse() { + return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); +} + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +/* ps start + +*/ + +uniform bool Invert_Direction< + string label = "Invert Direction"; +> = true; + +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float Ratio< + string label = "Ratio"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 4.0; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100; // +uniform bool Apply_To_Alpha_Layer = true; + +#define PI2 6.28318530718 +#define PI 3.1416 + + +float vorocloud(float2 p){ + float f = 0.0; + float flow = 1.0; + float time = elapsed_time; + if(Invert_Direction){ + flow *= -1; + } + /* + //periodically stop + if (loops % 16 >= 8.0) + { + time = local_time - elapsed_time; + } + */ + + float r = clamp(Ratio,-50,50); + float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); + p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); + + float2 pts[4]; + + pts[0] = float2(0.5, 0.6); + pts[1] = float2(-0.4, 0.4); + pts[2] = float2(0.2, -0.7); + pts[3] = float2(-0.3, -0.4); + + float d = 5.0; + + for(int i = 0; i < 4; i++){ + pts[i].x += 0.03 * cos(float(i)) + p.x; + pts[i].y += 0.03 * sin(float(i)) + p.y; + d = min(d, distance(pts[i], pp)); + } + + f = 2.0 * pow(1.0 - 0.3 * d, 13.0); + + f = min(f, 1.0); + + return f; } +vec4 scene(float2 UV){ + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); -} + float x = UV.x; + float y = UV.y; + + float2 p = float2(x, y) - 0.5; + + vec4 col = vec4(0.0,0.0,0.0,0.0); + col.g += 0.02; + + float v = vorocloud(p); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.6 * v; + col.b += 0.5 * pow(v, 5.0); + + + v = vorocloud(p * 2.0); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.2 * v; + col.b += 0.01 * pow(v, 5.0); + + col.a = 1.0; + float luma = dot(col.rgb,float3(0.299,0.587,0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min,0.0,1.0); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputSettings { + float4 original_color = image.Sample(textureSampler, UV); + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + if (Apply_To_Alpha_Layer == false) + original_color.a = alpha; + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } + else + { + col.a = original_color.a; + } + return col; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] -[Alias('obs.powershell.websocket.GetInputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / uv_size; + fragColor = scene(uv); +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +/*ps end*/ -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} -process { +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47790,111 +43026,155 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputVolume { - +function Get-OBSMotionBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] -[Alias('obs.powershell.websocket.GetInputVolume')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the previous_output of OBSMotionBlurShader +[Alias('previous_output')] +[ComponentModel.DefaultBindingProperty('previous_output')] +[String] +$PreviousOutput, +# Set the strength of OBSMotionBlurShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'motion_blur' +$ShaderNoun = 'OBSMotionBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d previous_output; +uniform float strength< + string label = "strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +float4 mainImage(VertData v_in) : TARGET +{ + return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -47903,101 +43183,147 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLastReplayBufferReplay { - +function Get-OBSMultiplyShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] -[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the other_image of OBSMultiplyShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] +[String] +$OtherImage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'multiply' +$ShaderNoun = 'OBSMultiplyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d other_image; +float4 mainImage(VertData v_in) : TARGET +{ + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return base * other; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48006,317 +43332,511 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMediaInputStatus { - +function Get-OBSNightSkyShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] -[Alias('obs.powershell.websocket.GetMediaInputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the speed of OBSNightSkyShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the Include_Clouds of OBSNightSkyShader +[Alias('Include_Clouds')] +[ComponentModel.DefaultBindingProperty('Include_Clouds')] +[Management.Automation.SwitchParameter] +$IncludeClouds, +# Set the Include_Moon of OBSNightSkyShader +[Alias('Include_Moon')] +[ComponentModel.DefaultBindingProperty('Include_Moon')] +[Management.Automation.SwitchParameter] +$IncludeMoon, +# Set the center_width_percentage of OBSNightSkyShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSNightSkyShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# Set the Alpha_Percentage of OBSNightSkyShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Image of OBSNightSkyShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSNightSkyShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the number_stars of OBSNightSkyShader +[Alias('number_stars')] +[ComponentModel.DefaultBindingProperty('number_stars')] +[Int32] +$NumberStars, +# Set the SKY_COLOR of OBSNightSkyShader +[Alias('SKY_COLOR')] +[ComponentModel.DefaultBindingProperty('SKY_COLOR')] +[String] +$SKYCOLOR, +# Set the STAR_COLOR of OBSNightSkyShader +[Alias('STAR_COLOR')] +[ComponentModel.DefaultBindingProperty('STAR_COLOR')] +[String] +$STARCOLOR, +# Set the LIGHT_SKY of OBSNightSkyShader +[Alias('LIGHT_SKY')] +[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] +[String] +$LIGHTSKY, +# Set the SKY_LIGHTNESS of OBSNightSkyShader +[Alias('SKY_LIGHTNESS')] +[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] +[Single] +$SKYLIGHTNESS, +# Set the MOON_COLOR of OBSNightSkyShader +[Alias('MOON_COLOR')] +[ComponentModel.DefaultBindingProperty('MOON_COLOR')] +[String] +$MOONCOLOR, +# Set the moon_size of OBSNightSkyShader +[Alias('moon_size')] +[ComponentModel.DefaultBindingProperty('moon_size')] +[Single] +$MoonSize, +# Set the moon_bump_size of OBSNightSkyShader +[Alias('moon_bump_size')] +[ComponentModel.DefaultBindingProperty('moon_bump_size')] +[Single] +$MoonBumpSize, +# Set the Moon_Position_x of OBSNightSkyShader +[Alias('Moon_Position_x')] +[ComponentModel.DefaultBindingProperty('Moon_Position_x')] +[Single] +$MoonPositionX, +# Set the Moon_Position_y of OBSNightSkyShader +[Alias('Moon_Position_y')] +[ComponentModel.DefaultBindingProperty('Moon_Position_y')] +[Single] +$MoonPositionY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'night_sky' +$ShaderNoun = 'OBSNightSkyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 +// https://github.com/Oncorporation/obs-shaderfilter +//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 20.0; +uniform bool Include_Clouds = true; +uniform bool Include_Moon = true; +uniform int center_width_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 95.0; // +uniform bool Apply_To_Image = false; +uniform bool Replace_Image_Color = false; +uniform int number_stars< + string label = "Number stars"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; // +uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; +uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; +uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; +uniform float SKY_LIGHTNESS< + string label = "SKY LIGHTNESS"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = .3; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + // Moon +uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; +uniform float moon_size< + string label = "Moon size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.18; +uniform float moon_bump_size< + string label = "Moon bump size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.14; +uniform float Moon_Position_x< + string label = "Moon Position x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.6; +uniform float Moon_Position_y< + string label = "Moon Position y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.3; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +#define PI 3.1416 - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU +float noise11(float p) { + return frac(sin(p*633.1847) * 9827.95); +} + +float noise21(float2 p) { + return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +float2 noise22(float2 p) { + return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); +} + +//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise +float cosineInterpolation(float a, float b, float x) { + float ft = x * PI; + float f = (1. - cos(ft)) * .5; + return a * (1. - f) + b * f; +} + +float smoothNoise11(float p, float dist) { + float prev = noise11(p-dist); + float next = noise11(p+dist); + + return cosineInterpolation(prev, next, .5); +} + +float smoothNoise21(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + //smoothstep function: maybe change it later! + lv = lv*lv*(3.-2.*lv); + + float bl = noise21(id); + float br = noise21(id+float2(1.,0.)); + float b = lerp(bl, br, lv.x); + + float tl = noise21(id+float2(0.,1.)); + float tr = noise21(id+float2(1.,1.)); + float t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float2 smoothNoise22(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); + + lv = lv*lv*(3.-2.*lv); + + float2 bl = noise22(id); + float2 br = noise22(id+float2(1.,0.)); + float2 b = lerp(bl, br, lv.x); + + float2 tl = noise22(id+float2(0.,1.)); + float2 tr = noise22(id+float2(1.,1.)); + float2 t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); +} +float valueNoise11(float p) { + float c = smoothNoise11(p, 0.5); + c += smoothNoise11(p, 0.25)*.5; + c += smoothNoise11(p, 0.125)*.25; + c += smoothNoise11(p, 0.0625)*.125; + + return c /= .875; } +float valueNoise21(float2 uv) { + float c = smoothNoise21(uv, 4.); + c += smoothNoise21(uv, 8.)*.5; + c += smoothNoise21(uv, 16.)*.25; + c += smoothNoise21(uv, 32.)*.125; + c += smoothNoise21(uv, 64.)*.0625; + + return c /= .9375; +} -} +float2 valueNoise22(float2 uv) { + float2 c = smoothNoise22(uv, 4.); + c += smoothNoise22(uv, 8.)*.5; + c += smoothNoise22(uv, 16.)*.25; + c += smoothNoise22(uv, 32.)*.125; + c += smoothNoise22(uv, 64.)*.0625; + + return c /= .9375; +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSMonitor { +float3 points(float2 p, float2 uv, float3 color, float size, float blur) { + float dist = distance(p, uv); + return color*smoothstep(size, size*(0.999-blur), dist); +} +float mapInterval(float x, float a, float b, float c, float d) { + return (x-a)/(b-a) * (d-c) + c; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] -[Alias('obs.powershell.websocket.GetMonitorList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float blink(float time, float timeInterval) { + float halfInterval = timeInterval / 2.0; + //Get relative position in the bucket + float p = fmod(time, timeInterval); + + + if (p <= timeInterval / 2.) { + return smoothstep(0., 1., p/halfInterval); + } else { + return smoothstep(1., 0., (p-halfInterval)/halfInterval); + } +} +float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { + float dist = distance(p, uv); + float3 BumpSamples = float3(0.,0.,0.); + + if (dist < radius) { + float bumps = (1.-valueNoise21(uv*spin))*.1; + BumpSamples = float3(bumps, bumps, bumps); + } + return BumpSamples; +} -process { +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba;// = image.Sample(textureSampler, v_in.uv); + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st; + // Normalized pixel coordinates (from 0 to 1) + float2 uv = v_in.uv; + float2 ouv = uv; + uv -= .5; + uv.x *= uv_size.x/uv_size.y; + + float2 seed = toCenter / uv_size.xy; + + float time = elapsed_time + seed.x*speed; + + //float3 col = float3(0.0); + //float m = valueNoise21(uv); + float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); + + col *= SKY_LIGHTNESS - (1.-ouv.y); + + //Add clouds + if (Include_Clouds) + { + float2 timeUv = uv; + timeUv.x += time*.1; + timeUv.y += valueNoise11(timeUv.x+.352)*.01; + float cloud = valueNoise21(timeUv); + col += cloud*.1; + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + //Add stars in the top part of the scene + float timeInterval = speed *.5; //5.0 + float timeBucket = floor(time / timeInterval); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float2 moonPosition = float2(-1000, -1000); + if (Include_Moon) + { + moonPosition = float2(Moon_Position_x, Moon_Position_y); + col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); + // Moon bumps + col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + for (float i = 0.; i < clamp(number_stars,0,100); i++) { + float2 starPosition = float2(i/10., i/10.); + + starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); + starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); + + float starIntensity = blink(time+ (rand_f * i), timeInterval ); + //Hide stars that are behind the moon + if (distance(starPosition, moonPosition) > moon_size) { + col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; + col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; } + } + //col = float3(blink(time, timeInterval)); + rgba = float4(col,alpha); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + // Output to screen + if (Apply_To_Image) + { + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color,alpha); + + } + return rgba; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] -[Alias('obs.powershell.websocket.GetOutputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48325,106 +43845,155 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputSettings { - +function Get-OBSOpacityShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] -[Alias('obs.powershell.websocket.GetOutputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Opacity of OBSOpacityShader +[ComponentModel.DefaultBindingProperty('Opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'opacity' +$ShaderNoun = 'OBSOpacityShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Opacity shader - for OBS Shaderfilter +// Copyright 2021 by SkeltonBowTV +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv +uniform float Opacity< + string label = "Opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.01; +> = 100.00; // 0.00 - 100.00 percent - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage( VertData v_in ) : TARGET +{ + float4 color = image.Sample( textureSampler, v_in.uv ); + return float4( color.rgb, color.a * Opacity * 0.01 ); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48433,106 +44002,217 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputStatus { - +function Get-OBSPagePeelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] -[Alias('obs.powershell.websocket.GetOutputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Speed of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Position of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Position')] +[Single] +$Position, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'page-peel' +$ShaderNoun = 'OBSPagePeelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Page Peel, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Based on Shadertoy shader by droozle +// +// Description: +// +// +// Changelog: +// 0.01 - Initial release + +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 50.0; + float step = 0.001; +> = 1.00; +uniform float Position< + string label = "Position"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 0.0; +float4 mainImage( VertData v_in ) : TARGET +{ + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); + float2 uv = v_in.uv; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float t = Position + elapsed_time * Speed; + // Define the fold. + float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + // Sample texture. + float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. + + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + col *= 0.25; // Background color (peeled off). + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); + } + else { + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + float4 back = image.Sample( textureSampler, pt ); // Back color. + back.rgb = back.rgb * 0.25 + 0.75; + + float shadow = smoothstep( 0.0, 0.2, -side ); + back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); + + // Support for transparency. + col = lerp( col, back.rgb, back.a ); } + } + + // Output to screen + return float4(col,1.0); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48541,111 +44221,230 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPersistentData { - +function Get-OBSPagePeelTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] -[Alias('obs.powershell.websocket.GetPersistentData')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the image_a of OBSPagePeelTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSPagePeelTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSPagePeelTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSPagePeelTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the page_color of OBSPagePeelTransitionShader +[Alias('page_color')] +[ComponentModel.DefaultBindingProperty('page_color')] +[String] +$PageColor, +# Set the page_transparency of OBSPagePeelTransitionShader +[Alias('page_transparency')] +[ComponentModel.DefaultBindingProperty('page_transparency')] +[Single] +$PageTransparency, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'page-peel-transition' +$ShaderNoun = 'OBSPagePeelTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; +uniform float page_transparency< + string label = "Page Transparency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); + float2 uv = v_in.uv; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float t = transition_time * 12.0 + 11.0; + // Define the fold. + float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); + + // Sample texture. + float3 col = float3(1.0,0.0,0.0); + + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + // Next page + col = image_b.Sample( textureSampler, uv ).rgb; + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); + } + else { + + + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + col = image_a.Sample( textureSampler, pt ).rgb; // Back color. + col = lerp(page_color.rgb, col, page_transparency); + + float shadow = smoothstep( 0.0, 0.2, -side ); + col = lerp( col * 0.2, col, shadow ); + }else{ + col = image_a.Sample( textureSampler, uv ).rgb; + } + } + + // Output to screen + if(convert_linear) + col = srgb_nonlinear_to_linear(col); + return float4(col,1.0); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48654,214 +44453,429 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfile { - +function Get-OBSPerlinNoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] -[Alias('obs.powershell.websocket.GetProfileList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the animated of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('animated')] +[Management.Automation.SwitchParameter] +$Animated, +# Set the apply_to_channel of OBSPerlinNoiseShader +[Alias('apply_to_channel')] +[ComponentModel.DefaultBindingProperty('apply_to_channel')] +[Management.Automation.SwitchParameter] +$ApplyToChannel, +# Set the inverted of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('inverted')] +[Management.Automation.SwitchParameter] +$Inverted, +# Set the multiply of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('multiply')] +[Management.Automation.SwitchParameter] +$Multiply, +# Set the speed_horizonal of OBSPerlinNoiseShader +[Alias('speed_horizonal')] +[ComponentModel.DefaultBindingProperty('speed_horizonal')] +[Single] +$SpeedHorizonal, +# Set the speed_vertical of OBSPerlinNoiseShader +[Alias('speed_vertical')] +[ComponentModel.DefaultBindingProperty('speed_vertical')] +[Single] +$SpeedVertical, +# Set the iterations of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('iterations')] +[Int32] +$Iterations, +# Set the white_noise of OBSPerlinNoiseShader +[Alias('white_noise')] +[ComponentModel.DefaultBindingProperty('white_noise')] +[Single] +$WhiteNoise, +# Set the black_noise of OBSPerlinNoiseShader +[Alias('black_noise')] +[ComponentModel.DefaultBindingProperty('black_noise')] +[Single] +$BlackNoise, +# Set the notes of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'perlin_noise' +$ShaderNoun = 'OBSPerlinNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// +// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +// +// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson +// Translation and modification was made by Keijiro Takahashi. +// Conversion for OBS by Charles Fettinger. +// +// This shader is based on the webgl-noise GLSL shader. For further details +// of the original shader, please see the following description from the +// original source code. +// + // +// GLSL textureless classic 2D noise "cnoise", (white_noise) +// with an RSL-style periodic variant "pnoise" (black_noise). +// Author: Stefan Gustavson (stefan.gustavson@liu.se) +// Version: 2011-08-22 +// +// Many thanks to Ian McEwan of Ashima Arts for the +// ideas for permutation and gradient selection. +// +// Copyright (c) 2011 Stefan Gustavson. All rights reserved. +// Distributed under the MIT license. See LICENSE file. +// https://github.com/ashima/webgl-noise +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 + float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + float4 mod289(float4 x) +{ + return x - floor(x / 289.0) * 289.0; +} + float4 permute(float4 x) +{ + return mod289(((x*34.0)+1.0)*x); +} + float4 taylorInvSqrt(float4 r) +{ + return 1.79284291400159 - r * 0.85373472095314; +} + float2 fade(float2 t) { + return t*t*t*(t*(t*6.0-15.0)+10.0); +} + // Classic Perlin noise +float cnoise(float2 P) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + // Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + //The good bits~ adapting the noise generator for the plugin and giving some control over the shader + //todo: pseudorandom number generator w/ seed +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform bool animated; +uniform bool apply_to_channel; +uniform bool inverted; +uniform bool multiply; +uniform float speed_horizonal< + string label = "Speed horizontal"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform float speed_vertical< + string label = "Speed vertical"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0; +uniform int iterations< + string label = "Iterations"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 4; +//how much c_noise do we want? white +uniform float white_noise< + string label = "White noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +//how much p_noise do we want? black +uniform float black_noise< + string label = "Black noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform string notes< + string widget_type = "info"; +> = "white noise and black noise and iterations.. enjoy!"; + float2 noisePosition(float t){ + return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); +} + float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float t = elapsed_time * speed; + float2 dir = float2(speed_horizonal,speed_vertical); + + if(!animated){ + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = v_in.uv * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + //o += pnoise(coord, period) * w; + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } + } else { + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = (v_in.uv + t*dir) * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfileParameter { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] -[Alias('obs.powershell.websocket.GetProfileParameter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -48870,307 +44884,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordDirectory { - +function Get-OBSPerspectiveShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] -[Alias('obs.powershell.websocket.GetRecordDirectory')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the angle_x of OBSPerspectiveShader +[Alias('angle_x')] +[ComponentModel.DefaultBindingProperty('angle_x')] +[Single] +$AngleX, +# Set the angle_y of OBSPerspectiveShader +[Alias('angle_y')] +[ComponentModel.DefaultBindingProperty('angle_y')] +[Single] +$AngleY, +# Set the angle_z of OBSPerspectiveShader +[Alias('angle_z')] +[ComponentModel.DefaultBindingProperty('angle_z')] +[Single] +$AngleZ, +# Set the perspective of OBSPerspectiveShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the border_color of OBSPerspectiveShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the show_border of OBSPerspectiveShader +[Alias('show_border')] +[ComponentModel.DefaultBindingProperty('show_border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'perspective' +$ShaderNoun = 'OBSPerspectiveShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Perspective Transform Shader for OBS +// Allows adjustable 3D perspective effects +// Usage: Add as filter in OBS via ShaderFilter plugin +uniform float angle_x< + string label = "X Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float angle_y< + string label = "Y Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float angle_z< + string label = "Z Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float perspective< + string label = "Perspective Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float4 border_color< + string label = "Border Color"; +> = {0.0, 0.0, 0.0, 1.0}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform bool show_border< + string label = "Show Border"; +> = true; +float4x4 rotationMatrix(float3 angles) +{ + float radX = radians(angles.x); + float radY = radians(angles.y); + float radZ = radians(angles.z); + + float sinX = sin(radX); + float cosX = cos(radX); + float sinY = sin(radY); + float cosY = cos(radY); + float sinZ = sin(radZ); + float cosZ = cos(radZ); + + return float4x4( + cosY*cosZ, -cosY*sinZ, sinY, 0, + sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, + -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, + 0, 0, 0, 1 + ); } - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] -[Alias('obs.powershell.websocket.GetRecordStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + + // Center coordinates + float2 center = float2(0.5, 0.5); + uv -= center; + + // Apply perspective + float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); + uv *= perspectiveFactor; + + // Create rotation matrix + float3 angles = float3(angle_x, angle_y, angle_z); + float4x4 rotMat = rotationMatrix(angles); + + // Apply transformation + float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); + + // Restore center position + uv = transformed.xy + center; + + // Sample texture with border handling + if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { + return show_border ? border_color : float4(0, 0, 0, 0); + } + + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSReplayBufferStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] -[Alias('obs.powershell.websocket.GetReplayBufferStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49179,204 +45142,402 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScene { - +function Get-OBSPieChartShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] -[Alias('obs.powershell.websocket.GetSceneList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the inner_radius of OBSPieChartShader +[Alias('inner_radius')] +[ComponentModel.DefaultBindingProperty('inner_radius')] +[Single] +$InnerRadius, +# Set the outer_radius of OBSPieChartShader +[Alias('outer_radius')] +[ComponentModel.DefaultBindingProperty('outer_radius')] +[Single] +$OuterRadius, +# Set the start_angle of OBSPieChartShader +[Alias('start_angle')] +[ComponentModel.DefaultBindingProperty('start_angle')] +[Single] +$StartAngle, +# Set the total of OBSPieChartShader +[ComponentModel.DefaultBindingProperty('total')] +[Int32] +$Total, +# Set the part_1 of OBSPieChartShader +[Alias('part_1')] +[ComponentModel.DefaultBindingProperty('part_1')] +[Int32] +$Part1, +# Set the color_1 of OBSPieChartShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the part_2 of OBSPieChartShader +[Alias('part_2')] +[ComponentModel.DefaultBindingProperty('part_2')] +[Int32] +$Part2, +# Set the color_2 of OBSPieChartShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the part_3 of OBSPieChartShader +[Alias('part_3')] +[ComponentModel.DefaultBindingProperty('part_3')] +[Int32] +$Part3, +# Set the color_3 of OBSPieChartShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the part_4 of OBSPieChartShader +[Alias('part_4')] +[ComponentModel.DefaultBindingProperty('part_4')] +[Int32] +$Part4, +# Set the color_4 of OBSPieChartShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# Set the part_5 of OBSPieChartShader +[Alias('part_5')] +[ComponentModel.DefaultBindingProperty('part_5')] +[Int32] +$Part5, +# Set the color_5 of OBSPieChartShader +[Alias('color_5')] +[ComponentModel.DefaultBindingProperty('color_5')] +[String] +$Color5, +# Set the part_6 of OBSPieChartShader +[Alias('part_6')] +[ComponentModel.DefaultBindingProperty('part_6')] +[Int32] +$Part6, +# Set the color_6 of OBSPieChartShader +[Alias('color_6')] +[ComponentModel.DefaultBindingProperty('color_6')] +[String] +$Color6, +# Set the part_7 of OBSPieChartShader +[Alias('part_7')] +[ComponentModel.DefaultBindingProperty('part_7')] +[Int32] +$Part7, +# Set the color_7 of OBSPieChartShader +[Alias('color_7')] +[ComponentModel.DefaultBindingProperty('color_7')] +[String] +$Color7, +# Set the part_8 of OBSPieChartShader +[Alias('part_8')] +[ComponentModel.DefaultBindingProperty('part_8')] +[Int32] +$Part8, +# Set the color_8 of OBSPieChartShader +[Alias('color_8')] +[ComponentModel.DefaultBindingProperty('color_8')] +[String] +$Color8, +# Set the part_9 of OBSPieChartShader +[Alias('part_9')] +[ComponentModel.DefaultBindingProperty('part_9')] +[Int32] +$Part9, +# Set the color_9 of OBSPieChartShader +[Alias('color_9')] +[ComponentModel.DefaultBindingProperty('color_9')] +[String] +$Color9, +# Set the part_10 of OBSPieChartShader +[Alias('part_10')] +[ComponentModel.DefaultBindingProperty('part_10')] +[Int32] +$Part10, +# Set the color_10 of OBSPieChartShader +[Alias('color_10')] +[ComponentModel.DefaultBindingProperty('color_10')] +[String] +$Color10, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pie-chart' +$ShaderNoun = 'OBSPieChartShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float inner_radius< + string label = "inner radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 32.0; +uniform float outer_radius< + string label = "outer radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 50.0; +uniform float start_angle< + string label = "Start angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 90.0; +uniform int total< + string label = "Total"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 100; +uniform int part_1< + string label = "Part 1"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 50; +uniform float4 color_1 = {0.0,0.26,0.62,1.0}; +uniform int part_2< + string label = "Part 2"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 25; +uniform float4 color_2 = {0.24,0.40,0.68,1.0}; +uniform int part_3< + string label = "Part 3"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 10; +uniform float4 color_3 = {0.38,0.56,0.75,1.0}; +uniform int part_4< + string label = "Part 4"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 5; +uniform float4 color_4 = {0.52,0.72,0.81,1.0}; +uniform int part_5< + string label = "Part 5"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 3; +uniform float4 color_5 = {0.69,0.87,0.86,1.0}; +uniform int part_6< + string label = "Part 6"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 2; +uniform float4 color_6 = {1.0,0.79,0.73,1.0}; +uniform int part_7< + string label = "Part 7"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_7 = {0.99,0.57,0.57,1.0}; +uniform int part_8< + string label = "Part 8"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_8 = {0.91,0.36,0.44,1.0}; +uniform int part_9< + string label = "Part 9"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_9 = {0.77,0.16,0.32,1.0}; +uniform int part_10< + string label = "Part 10"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 0; +uniform float4 color_10 = {0.58,0.0,0.23,1.0}; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + const float pi = 3.14159265358979323846; +#ifdef OPENGL + float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); + float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); +#else + float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; + float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; +#endif + float2 center = float2(0.5, 0.5); + float2 factor; + if(uv_size.x < uv_size.y){ + factor = float2(1.0, uv_size.y/uv_size.x); + }else{ + factor = float2(uv_size.x/uv_size.y, 1.0); + } + center = center * factor; + float d = distance(center, v_in.uv * factor); + if(d > outer_radius/100.0 || d < inner_radius/100.0){ + return image.Sample(textureSampler, v_in.uv); + } + float2 toCenter = center - v_in.uv*factor; + float angle = atan2(toCenter.y ,toCenter.x); + angle = angle - (start_angle / 180.0 * pi); + if(angle < 0.0) + angle = pi + pi + angle; + if(angle < 0.0) + angle = pi + pi + angle; + angle = angle / (pi + pi); + float t = 0.0; + for(int i = 0; i < 10; i+=1) { + float part = parts[i]/total; + if(angle > t && angle <= t+part){ + return colors[i]; } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + t = t + part; + } + return image.Sample(textureSampler, v_in.uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneCollection { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] -[Alias('obs.powershell.websocket.GetSceneCollectionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49385,111 +45546,192 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItem { - +function Get-OBSPixelationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] -[Alias('obs.powershell.websocket.GetSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Target_Width of OBSPixelationShader +[Alias('Target_Width')] +[ComponentModel.DefaultBindingProperty('Target_Width')] +[Single] +$TargetWidth, +# Set the Target_Height of OBSPixelationShader +[Alias('Target_Height')] +[ComponentModel.DefaultBindingProperty('Target_Height')] +[Single] +$TargetHeight, +# Set the notes of OBSPixelationShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pixelation' +$ShaderNoun = 'OBSPixelationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// with help from SkeltonBowTV +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 15, 2022 +uniform float Target_Width< + string label = "Target Width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 320.0; +uniform float Target_Height< + string label = "Target Height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 180.0; +uniform string notes< + string widget_type = "info"; +> = "adjust width and height to your screen dimension"; +float4 mainImage(VertData v_in) : TARGET +{ + float targetWidth = Target_Width; + if(targetWidth < 2.0) + targetWidth = 2.0; + float targetHeight = Target_Height; + if(targetHeight < 2.0) + targetHeight = 2.0; + float2 tex1; + int pixelSizeX = int(uv_size.x / targetWidth); + int pixelSizeY = int(uv_size.y / targetHeight); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + int pixelX = int(v_in.uv.x * uv_size.x); + int pixelY = int(v_in.uv.y * uv_size.y); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; + tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + float4 c1 = image.Sample(textureSampler, tex1 ); + + return c1; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49498,117 +45740,210 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemBlendMode { - +function Get-OBSPixelationTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the transition_time of OBSPixelationTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSPixelationTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the power of OBSPixelationTransitionShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the center_x of OBSPixelationTransitionShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSPixelationTransitionShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pixelation-transition' +$ShaderNoun = 'OBSPixelationTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float power< + string label = "Power"; + string widget_type = "slider"; + float minimum = 0.5; + float maximum = 8.0; + float step = 0.01; +> = 3.0; +uniform float center_x< + string label = "X"; + string widget_type = "slider"; + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Y"; + string widget_type = "slider"; + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + //1..0..1 + float scale = abs(transition_time - 0.5) * 2.0; + scale = pow(scale, power); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float2 uv = v_in.uv; + uv -= float2(center_x, center_y); + uv *= uv_size; + uv *= scale; + uv = floor(uv); + uv /= scale; + uv /= uv_size; + uv += float2(center_x, center_y); + uv = clamp(uv, 1.0/uv_size, 1.0); + float4 rgba = image.Sample(textureSampler, uv); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49617,117 +45952,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemEnabled { - +function Get-OBSPolarShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] -[Alias('obs.powershell.websocket.GetSceneItemEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the center_x of OBSPolarShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSPolarShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the point_y of OBSPolarShader +[Alias('point_y')] +[ComponentModel.DefaultBindingProperty('point_y')] +[Single] +$PointY, +# Set the flip of OBSPolarShader +[ComponentModel.DefaultBindingProperty('flip')] +[Management.Automation.SwitchParameter] +$Flip, +# Set the rotate of OBSPolarShader +[ComponentModel.DefaultBindingProperty('rotate')] +[Single] +$Rotate, +# Set the repeat of OBSPolarShader +[ComponentModel.DefaultBindingProperty('repeat')] +[Single] +$Repeat, +# Set the scale of OBSPolarShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'polar' +$ShaderNoun = 'OBSPolarShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.14159265359 +#define PI_2 6.2831 +#define mod(x,y) (x - y * floor(x / y)) +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float point_y< + string label = "Point y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform bool flip; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float rotate< + string label = "Rotate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float repeat< + string label = "Repeat"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; + +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv.x -= center_x ; + uv.y -= center_y ; + uv.x = uv.x * ( uv_size.x / uv_size.y); + float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; + if(repeat < 1.0){ + pixel_angle = mod(pixel_angle+rotate,1.0); + if(pixel_angle > repeat) + return float4(0,0,0,0); + pixel_angle = mod(pixel_angle/repeat,1.0); + } else { + pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); + } + float pixel_distance = length(uv)/ scale - point_y; + float2 uv2 = float2(pixel_angle , pixel_distance); + if(flip) + uv2 = float2(1.0,1.0) - uv2; + return image.Sample(textureSampler,uv2); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49736,122 +46192,259 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemId { +function Get-OBSPulseShader { + +[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] +param( +# Set the ViewProj of OBSPulseShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSPulseShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSPulseShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSPulseShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSPulseShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSPulseShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSPulseShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSPulseShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed of OBSPulseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the min_growth_pixels of OBSPulseShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] +[Single] +$MinGrowthPixels, +# Set the max_growth_pixels of OBSPulseShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] -[Alias('obs.powershell.websocket.GetSceneItemId')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +process { +$shaderName = 'pulse' +$ShaderNoun = 'OBSPulseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4x4 ViewProj; +uniform texture2d image; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 0.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 200.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('searchOffset')] -[ValidateRange(-1,[int]::MaxValue)] -[double] -$SearchOffset, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertData mainTransform(VertData v_in) +{ + VertData vert_out; -process { + float3 pos = v_in.pos.xyz; + float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); + float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; + float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; + float t = (1 + sin(elapsed_time * speed)) / 2; + float3 current_pos = min_pos * (1 - t) + max_pos * t; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49860,236 +46453,331 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemIndex { - +function Get-OBSRainbowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] -[Alias('obs.powershell.websocket.GetSceneItemIndex')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRainbowShader','Add-OBSRainbowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Saturation of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Saturation')] +[Single] +$Saturation, +# Set the Luminosity of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Luminosity')] +[Single] +$Luminosity, +# Set the Spread of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Spread')] +[Single] +$Spread, +# Set the Speed of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Alpha_Percentage of OBSRainbowShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Vertical of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the Rotational of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Rotational')] +[Management.Automation.SwitchParameter] +$Rotational, +# Set the Rotation_Offset of OBSRainbowShader +[Alias('Rotation_Offset')] +[ComponentModel.DefaultBindingProperty('Rotation_Offset')] +[Single] +$RotationOffset, +# Set the Apply_To_Image of OBSRainbowShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRainbowShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRainbowShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRainbowShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rainbow' +$ShaderNoun = 'OBSRainbowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Rainbow shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 13, 2022 +uniform float Saturation< + string label = "Saturation"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.8; // +uniform float Luminosity< + string label = "Luminosity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; // +uniform float Spread< + string label = "Spread"; + string widget_type = "slider"; + float minimum = 0.5; + float maximum = 10.0; + float step = 0.01; +> = 3.8; // +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 2.4; // +uniform float Alpha_Percentage< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; // +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 6.28318531; + float step = 0.001; +> = 0.0; // +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; +float hueToRGB(float v1, float v2, float vH) { + vH = frac(vH); + if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); + if ((2.0 * vH) < 1.0) return (v2); + if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); + return clamp(v1, 0.0, 1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float4 HSLtoRGB(float4 hsl) { + float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); + float v1 = 0.0; + float v2 = 0.0; + + if (hsl.y == 0) { + rgb.xyz = hsl.zzz; + } + else { + + if (hsl.z < 0.5) { + v2 = hsl.z * (1 + hsl.y); + } + else { + v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + } + + v1 = 2.0 * hsl.z - v2; + + rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); + rgb.y = hueToRGB(v1, v2, hsl.x); + rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); + + } + + return rgb; } +float4 mainImage(VertData v_in) : TARGET +{ + float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemLocked { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] -[Alias('obs.powershell.websocket.GetSceneItemLocked')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //set colors and direction + float hue = (-1 * lPos.x) / 2.0; + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + hue = (lPos.x * cosine + lPos.y * sine) * 0.5; + } -process { + if (Vertical && (Rotational == false)) + { + hue = (-1 * lPos.y) * 0.5; + } + hue += time; + hue = frac(hue); + float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); + float4 rgba = HSLtoRGB(hsl); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; + float4 luma_color = float4(luma, luma, luma, luma); + if (Replace_Image_Color) + color = luma_color; + rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50098,349 +46786,396 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemSource { - +function Get-OBSRainWindowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] -[Alias('obs.powershell.websocket.GetSceneItemSource')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the size of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('size')] +[Single] +$Size, +# Set the blurSize of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('blurSize')] +[Single] +$BlurSize, +# Set the trail_strength of OBSRainWindowShader +[Alias('trail_strength')] +[ComponentModel.DefaultBindingProperty('trail_strength')] +[Single] +$TrailStrength, +# Set the trail_color of OBSRainWindowShader +[Alias('trail_color')] +[ComponentModel.DefaultBindingProperty('trail_color')] +[Single] +$TrailColor, +# Set the speed of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the debug of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('debug')] +[Management.Automation.SwitchParameter] +$DebugShader, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rain-window' +$ShaderNoun = 'OBSRainWindowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro +// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 +// https://www.shadertoy.com/view/ltffzl +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +uniform float size< + string label = "Rain Drop Size"; + string widget_type = "slider"; + float minimum = 0.001; + float maximum = 0.5; + float step = 0.01; +> = 0.2; +uniform float blurSize< + string label = "Blur Radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 32.0; // BLUR SIZE (Radius) +uniform float trail_strength< + string label = "Trail Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform float trail_color< + string label = "Trail Color"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 40.0; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.01; +> = 100.0; +uniform bool debug = false; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float fract(float v){ + return v - floor(v); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float3 fract3(float3 v){ + return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float3 fract4(float4 v){ + return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); +} + + +float3 N13(float p) { + // from DAVE HOSKINS + float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yzx + 19.19); + return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); +} + +float4 N14(float t) { + return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); +} +float N(float t) { + return fract(sin(t*12345.564)*7658.76); +} + +float Saw(float b, float t) { + return smoothstep(0., b, t)*smoothstep(1., b, t); +} +float2 Drops(float2 uv, float t) { + + float2 UV = uv; + + // DEFINE GRID + uv.y += t*0.8; + float2 a = float2(6., 1.); + float2 grid = a*2.; + float2 id = floor(uv*grid); + + // RANDOM SHIFT Y + float colShift = N(id.x); + uv.y += colShift; + + // DEFINE SPACES + id = floor(uv*grid); + float3 n = N13(id.x*35.2+id.y*2376.1); + float2 st = fract2(uv*grid)-float2(.5, 0); + + // POSITION DROPS + //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 + float x = n.x-.5; + + float y = UV.y*20.; + + float distort = sin(y+sin(y)); + x += distort*(.5-abs(x))*(n.z-.5); + x *= .7; + float ti = fract(t+n.z); + y = (Saw(.85, ti)-.5)*.9+.5; + float2 p = float2(x, y); + + // DROPS + float d = length((st-p)*a.yx); + + float dSize = size; + + float Drop = smoothstep(dSize, .0, d); + + + float r = sqrt(smoothstep(1., y, st.y)); + float cd = abs(st.x-x); + + // TRAILS + float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); + float trailFront = smoothstep(-.02, .02, st.y-y); + trail *= trailFront; + + + // DROPLETS + y = UV.y; + y += N(id.x); + float trail2 = smoothstep(dSize*r, .0, cd); + float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; + y = fract(y*10.)+(st.y-.5); + float dd = length(st-float2(x, y)); + droplets = smoothstep(dSize*N(id.x), 0., dd); + float m = Drop+droplets*r*trailFront; + if(debug){ + m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES + } + + + return float2(m, trail); } +float StaticDrops(float2 uv, float t) { + uv *= 30.; + + float2 id = floor(uv); + uv = fract2(uv)-.5; + float3 n = N13(id.x*107.45+id.y*3543.654); + float2 p = (n.xy-.5)*0.5; + float d = length(uv-p); + + float fade = Saw(.025, fract(t+n.z)); + float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemTransform { + return c; +} +float2 Rain(float2 uv, float t) { + //float s = StaticDrops(uv, t); + float2 r1 = Drops(uv, t); + float2 r2 = Drops(uv*1.8, t); + float c; + if(debug){ + c = r1.x; + }else{ + c = r1.x+r2.x;//s+r1.x+r2.x; + } + + c = smoothstep(.3, 1., c); + + if(debug){ + return float2(c, r1.y); + }else{ + return float2(c, max(r1.y, r2.y)); + } +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] -[Alias('obs.powershell.websocket.GetSceneItemTransform')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; + uv.y = 1.0 - uv.y; + uv = uv * uv_scale; + float2 UV = v_in.uv; + float T = elapsed_time * speed / 100.0; + + float t = T*.2; + + UV = (UV-.5)*(.9)+.5; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + float2 c = Rain(uv, t); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + float2 e = float2(.001, 0.); //pixel offset + float cx = Rain(uv+e, t).x; + float cy = Rain(uv+e.yx, t).x; + float2 n = float2(cx-c.x, cy-c.x); //normals -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj + float Pi = 6.28318530718; // Pi*2 + // GAUSSIAN BLUR SETTINGS {{{ + float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) + // GAUSSIAN BLUR SETTINGS }}} + float2 Radius = blurSize/uv_size; + float3 col = image.Sample(textureSampler, UV).rgb; -process { + if(blurSize > 0.0){ + // Blur calculations + for(float d=0.0; dAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneSceneTransitionOverride { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50449,101 +47184,199 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneTransition { - +function Get-OBSRectangularDropShadowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] -[Alias('obs.powershell.websocket.GetSceneTransitionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRectangularDropShadowShader','Add-OBSRectangularDropShadowShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the shadow_offset_x of OBSRectangularDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSRectangularDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSRectangularDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the shadow_color of OBSRectangularDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] +[String] +$ShadowColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rectangular_drop_shadow' +$ShaderNoun = 'OBSRectangularDropShadowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 22, 2022 +uniform int shadow_offset_x< + string label = "shadow offset x"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +>; +uniform int shadow_offset_y< + string label = "shadow offset y"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 1; +uniform float4 shadow_color; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); + float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); + float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); + + float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); + float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); + float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); + + return final_shadow_color * (1-color.a) + color; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50552,111 +47385,204 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceActive { - +function Get-OBSReflectShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] -[Alias('obs.powershell.websocket.GetSourceActive')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] param( - +# Set the Horizontal of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the Vertical of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the center_x_percent of OBSReflectShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSReflectShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Reflect' +$ShaderNoun = 'OBSReflectShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Reflect Shader +// Reflects horizontally and/or vertically. - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool Horizontal< + string label = "Reflect horizontally"; +> = false; +uniform bool Vertical< + string label = "Reflect vertically"; +> = true; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int center_x_percent< + string label = "center x percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + + if (Horizontal == true) { + if (pos.x < center_pos.x) { + pos.x = center_pos.x - pos.x; + } else if (pos.x == center_pos.x) { + pos.x = pos.x; + } else { + pos.x = pos.x - center_pos.x; + } + } + if (Vertical == true) { + if (pos.y < center_pos.y) { + pos.y = center_pos.y - pos.y; + } else if (pos.y == center_pos.y) { + pos.y = pos.y; + } else { + pos.y = pos.y - center_pos.y; + } + } + + return image.Sample(textureSampler, pos); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50665,116 +47591,169 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilter { - +function Get-OBSRemovePartialPixelsShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] -[Alias('obs.powershell.websocket.GetSourceFilter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] param( - +# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the notes of OBSRemovePartialPixelsShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'remove_partial_pixels' +$ShaderNoun = 'OBSRemovePartialPixelsShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int minimum_alpha_percent< + string label = "minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + else + { + return float4(output_color); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50783,106 +47762,204 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterDefaultSettings { +function Get-OBSRepeatGridCenterCropShader { - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] -[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the alpha of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('alpha')] +[Single] +$Alpha, +# Set the columns of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('columns')] +[Single] +$Columns, +# Set the rows of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('rows')] +[Single] +$Rows, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat_grid_center_crop' +$ShaderNoun = 'OBSRepeatGridCenterCropShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// repeat_grid_center_crop.effect +uniform float4x4 ViewProj; +uniform texture2d image; +sampler_state def_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float alpha = 1.0; +uniform float columns = 3.0; +uniform float rows = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +VertInOut VSDefault(VertInOut vert_in) { + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1), ViewProj); + vert_out.uv = vert_in.uv * float2(columns, rows); + return vert_out; +} + +float4 PSMain(VertInOut vert_in) : TARGET { + // Calculate fractional UV within grid cell + float2 cell_uv = frac(vert_in.uv); + + // Calculate center crop parameters + float horizontalCropStart = 0.5 - 0.5/columns; + + // Map to centered portion of original image + float2 original_uv = float2( + cell_uv.x / columns + horizontalCropStart, + cell_uv.y / rows + ); + + // Sample the image + float4 col = image.Sample(def_sampler, original_uv); + col.a *= alpha; + return col; +} + +technique Draw { + pass { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSMain(vert_in); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50891,101 +47968,267 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterKind { - +function Get-OBSRepeatShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] -[Alias('obs.powershell.websocket.GetSourceFilterKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatShader','Add-OBSRepeatShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the color_matrix of OBSRepeatShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color_range_min of OBSRepeatShader +[Alias('color_range_min')] +[ComponentModel.DefaultBindingProperty('color_range_min')] +[Single[]] +$ColorRangeMin, +# Set the color_range_max of OBSRepeatShader +[Alias('color_range_max')] +[ComponentModel.DefaultBindingProperty('color_range_max')] +[Single[]] +$ColorRangeMax, +# Set the image of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSRepeatShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRepeatShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRepeatShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRepeatShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSRepeatShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSRepeatShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the alpha of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('alpha')] +[Single] +$Alpha, +# Set the copies of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('copies')] +[Single] +$Copies, +# Set the notes of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat' +$ShaderNoun = 'OBSRepeatShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +uniform float4x4 ViewProj; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; +uniform texture2d image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +uniform float alpha< + string label = "Alpha"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 3.0; + float step = 0.001; +> = 1.0; +uniform float copies< + string label = "Copies"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; +uniform string notes< + string widget_type = "info"; +> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; + +sampler_state def_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; + +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertInOut VSDefault(VertInOut vert_in) +{ + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv = vert_in.uv * sqrt(copies); + return vert_out; +} + +float4 PSDrawBare(VertInOut vert_in) : TARGET +{ + float4 rgba = image.Sample(def_sampler, vert_in.uv); + rgba.a *= alpha; + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } +} + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50994,350 +48237,300 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterList { - +function Get-OBSRepeatTextureShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] -[Alias('obs.powershell.websocket.GetSourceFilterList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] param( - +# Set the ViewProj of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the color_matrix of OBSRepeatTextureShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color_range_min of OBSRepeatTextureShader +[Alias('color_range_min')] +[ComponentModel.DefaultBindingProperty('color_range_min')] +[Single[]] +$ColorRangeMin, +# Set the color_range_max of OBSRepeatTextureShader +[Alias('color_range_max')] +[ComponentModel.DefaultBindingProperty('color_range_max')] +[Single[]] +$ColorRangeMax, +# Set the image of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the tex_image of OBSRepeatTextureShader +[Alias('tex_image')] +[ComponentModel.DefaultBindingProperty('tex_image')] +[String] +$TexImage, +# Set the elapsed_time of OBSRepeatTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRepeatTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRepeatTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRepeatTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSRepeatTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSRepeatTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the blend of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('blend')] +[Single] +$Blend, +# Set the copies of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('copies')] +[Single] +$Copies, +# Set the notes of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the alpha_percentage of OBSRepeatTextureShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Single] +$AlphaPercentage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat_texture' +$ShaderNoun = 'OBSRepeatTextureShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +uniform float4x4 ViewProj; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; +uniform texture2d image; +uniform texture2d tex_image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceScreenshot { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] -[Alias('obs.powershell.websocket.GetSourceScreenshot')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, +uniform float blend< + string label = "Blend"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 3.0; + float step = 0.001; +> = 1.0; +uniform float copies< + string label = "Copies"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; +uniform string notes< + string widget_type = "info"; +> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; +uniform float alpha_percentage< + string label = "alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, +sampler_state tex_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, +sampler_state base_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +struct VertIn { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; +struct VertOut { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; -process { +VertOut VSDefault(VertIn vert_in) +{ + VertOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv_1 = vert_in.uv_0; + vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); + return vert_out; +} +float4 PSDrawBare(VertOut vert_in) : TARGET +{ + float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); + float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); + float4 base = image.Sample(base_sampler, vert_in.uv_1); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return (1 - alpha) * base + (alpha) * tex; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecialInputs { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] -[Alias('obs.powershell.websocket.GetSpecialInputs')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51346,101 +48539,199 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStats { - +function Get-OBSRGBAPercentShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStats')] -[Alias('obs.powershell.websocket.GetStats')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the RedPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('RedPercent')] +[Single] +$RedPercent, +# Set the GreenPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('GreenPercent')] +[Single] +$GreenPercent, +# Set the BluePercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('BluePercent')] +[Single] +$BluePercent, +# Set the AlphaPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('AlphaPercent')] +[Single] +$AlphaPercent, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'RGBA_Percent' +$ShaderNoun = 'OBSRGBAPercentShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple RGBA Percent Shader +// Allows Red, Green, or Blue to be adjusted between 0-200% +// Allows Alpha to be adjusted between 0/100% +uniform float RedPercent< + string label = "Red percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; +uniform float GreenPercent< + string label = "Green percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float BluePercent< + string label = "Blue percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200; + float step = 1.0; +> = 100.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float AlphaPercent< + string label = "Alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + + float4 imageColors = image.Sample(textureSampler, v_in.uv); + float4 adjustedColor = float4( + imageColors.r * (RedPercent * 0.01), + imageColors.g * (GreenPercent * 0.01), + imageColors.b * (BluePercent * 0.01), + imageColors.a * (AlphaPercent * 0.01) + ); + return adjustedColor; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51449,204 +48740,268 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamServiceSettings { - +function Get-OBSRgbColorWheelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] -[Alias('obs.powershell.websocket.GetStreamServiceSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed of OBSRgbColorWheelShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the color_depth of OBSRgbColorWheelShader +[Alias('color_depth')] +[ComponentModel.DefaultBindingProperty('color_depth')] +[Single] +$ColorDepth, +# Set the Apply_To_Image of OBSRgbColorWheelShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRgbColorWheelShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRgbColorWheelShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Alpha_Percentage of OBSRgbColorWheelShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the center_width_percentage of OBSRgbColorWheelShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRgbColorWheelShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) -process { +process { +$shaderName = 'rgb_color_wheel' +$ShaderNoun = 'OBSRgbColorWheelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 15.0; + float step = 0.1; +> = 2.0; +uniform float color_depth< + string label = "Color Depth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.1; +> = 2.10; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100; // +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; + +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.14159265f;//acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st ; + float r = length(toCenter) * color_depth; + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(speed ,18)) / 18; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] -[Alias('obs.powershell.websocket.GetStreamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51655,101 +49010,212 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStudioModeEnabled { - +function Get-OBSRgbSplitShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] -[Alias('obs.powershell.websocket.GetStudioModeEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbSplitShader','Add-OBSRgbSplitShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the redx of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('redx')] +[Single] +$Redx, +# Set the redy of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('redy')] +[Single] +$Redy, +# Set the greenx of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('greenx')] +[Single] +$Greenx, +# Set the greeny of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('greeny')] +[Single] +$Greeny, +# Set the bluex of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('bluex')] +[Single] +$Bluex, +# Set the bluey of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('bluey')] +[Single] +$Bluey, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rgb_split' +$ShaderNoun = 'OBSRgbSplitShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float redx< + string label = "Red X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 2.00; +uniform float redy< + string label = "Red Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greenx< + string label = "Green X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greeny< + string label = "Green Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float bluex< + string label = "Blue X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = -2.00; +uniform float bluey< + string label = "Blue Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + if(redx != 0.0 || redy != 0.0) + c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; + if(greenx != 0.0 || greeny != 0.0) + c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; + if(bluex != 0.0 || bluey != 0.0) + c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51758,101 +49224,225 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTransitionKind { - +function Get-OBSRgbvisibilityShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetTransitionKindList')] -[Alias('obs.powershell.websocket.GetTransitionKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbvisibilityShader','Add-OBSRgbvisibilityShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Red of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Red')] +[Single] +$Red, +# Set the Green of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Green')] +[Single] +$Green, +# Set the Blue of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the RedVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('RedVisibility')] +[Single] +$RedVisibility, +# Set the GreenVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('GreenVisibility')] +[Single] +$GreenVisibility, +# Set the BlueVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('BlueVisibility')] +[Single] +$BlueVisibility, +# Set the notes of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rgbvisibility' +$ShaderNoun = 'OBSRgbvisibilityShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGB visibility separation filter, created by EposVox +uniform float Red< + string label = "Red"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Green< + string label = "Green"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float Blue< + string label = "Blue"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float RedVisibility< + string label = "Red Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float GreenVisibility< + string label = "Green Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float BlueVisibility< + string label = "Blue Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform string notes< + string widget_type = "info"; +> = "Modify Colors to correct for gamma, use equal values for general correction."; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float redChannel = pow(c.r, Red) * RedVisibility; + float greenChannel = pow(c.g, Green) * GreenVisibility; + float blueChannel = pow(c.b, Blue) * BlueVisibility; + + return float4(redChannel, greenChannel, blueChannel, c.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51861,204 +49451,291 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVersion { - +function Get-OBSRGSSAAShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVersion')] -[Alias('obs.powershell.websocket.GetVersion')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ColorSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('ColorSigma')] +[Single] +$ColorSigma, +# Set the SpatialSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('SpatialSigma')] +[Single] +$SpatialSigma, +# Set the notes of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'RGSSAA' +$ShaderNoun = 'OBSRGSSAAShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 +// https://github.com/exeldro/obs-shaderfilter/tree/master +// Using edge detection shader as a base, created by Hallatore +// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 +uniform float ColorSigma< + string label = "Color Sigma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float SpatialSigma< + string label = "Spatial Sigma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; +> = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform string notes< + string widget_type = "info"; +> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float Luminance(float3 rgb) +{ + return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float4 mainImage(VertData v_in) : TARGET +{ + float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; + float2 SceneUV = v_in.uv; + float2 TexelScale = 1.0f/uv_size; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + const float SQRT2 = 1.4142135624; + const float PI = 3.141592654; + const float angle = PI / 8.0; + const float cs = cos(angle); + const float sn = sin(angle); -} + // Rotated grid samples + float3 C1 = + image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; + float3 C2 = + image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; + float3 C3 = + image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; + float3 C4 = + image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; + float3 C5 = + image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; + float3 C6 = + image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; + float3 C7 = + image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; + float3 C8 = + image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; + // Luminance edge detection + float A0 = Luminance(SceneColor); + float CL1 = Luminance(C1); + float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); + float CL2 = Luminance(C2); + float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); + float CL3 = Luminance(C3); + float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); + float CL4 = Luminance(C4); + float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); + float CL5 = Luminance(C5); + float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); + float CL6 = Luminance(C6); + float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); + float CL7 = Luminance(C7); + float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); + float CL8 = Luminance(C8); + float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); + float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); -} + // Calculate distance-based weights + float2 Dist1 = float2(cs, -sn); + float2 Dist2 = float2(-cs, -sn); + float2 Dist3 = float2(-sn, cs); + float2 Dist4 = float2(sn, cs); + float2 Dist5 = float2(cs * SQRT2, 0); + float2 Dist6 = float2(0, sn * SQRT2); + float2 Dist7 = float2(-cs * SQRT2, 0); + float2 Dist8 = float2(0, -sn * SQRT2); + float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); + float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); + float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); + float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); + float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); + float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); + float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); + float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSVideoSettings { + // Color weights + float3 ColorDiff1 = SceneColor.rgb - C1; + float3 ColorDiff2 = SceneColor.rgb - C2; + float3 ColorDiff3 = SceneColor.rgb - C3; + float3 ColorDiff4 = SceneColor.rgb - C4; + float3 ColorDiff5 = SceneColor.rgb - C5; + float3 ColorDiff6 = SceneColor.rgb - C6; + float3 ColorDiff7 = SceneColor.rgb - C7; + float3 ColorDiff8 = SceneColor.rgb - C8; + float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); + float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); + float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); + float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); + float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); + float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); + float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); + float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] -[Alias('obs.powershell.websocket.GetVideoSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Mixing weights + float W1 = SW1 * CW1; + float W2 = SW2 * CW2; + float W3 = SW3 * CW3; + float W4 = SW4 * CW4; + float W5 = SW5 * CW5; + float W6 = SW6 * CW6; + float W7 = SW7 * CW7; + float W8 = SW8 * CW8; + float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; + // Weighted color + float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + + C7 * W7 + C8 * W8) / + max(TotalWeight, 0.0001); -process { + // Blend it + float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); + float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); + float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); + float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); + float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); + float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); + float HDREdge = max(A0Max, A0Max2); + float EdgeMask = saturate(1.0f - HDREdge); + return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52067,101 +49744,202 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVirtualCamStatus { - +function Get-OBSRippleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] -[Alias('obs.powershell.websocket.GetVirtualCamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the distance_factor of OBSRippleShader +[Alias('distance_factor')] +[ComponentModel.DefaultBindingProperty('distance_factor')] +[Single] +$DistanceFactor, +# Set the time_factor of OBSRippleShader +[Alias('time_factor')] +[ComponentModel.DefaultBindingProperty('time_factor')] +[Single] +$TimeFactor, +# Set the power_factor of OBSRippleShader +[Alias('power_factor')] +[ComponentModel.DefaultBindingProperty('power_factor')] +[Single] +$PowerFactor, +# Set the center_pos_x of OBSRippleShader +[Alias('center_pos_x')] +[ComponentModel.DefaultBindingProperty('center_pos_x')] +[Single] +$CenterPosX, +# Set the center_pos_y of OBSRippleShader +[Alias('center_pos_y')] +[ComponentModel.DefaultBindingProperty('center_pos_y')] +[Single] +$CenterPosY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'ripple' +$ShaderNoun = 'OBSRippleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float distance_factor< + string label = "distance factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 12.0; +uniform float time_factor< + string label = "time factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform float power_factor< + string label = "power factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 3.0; +uniform float center_pos_x< + string label = "center pos x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float center_pos_y< + string label = "center pos y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 cPos = (v_in.uv * 2 ) -1; + float2 center_pos = float2(center_pos_x, center_pos_y); + float cLength = distance(cPos, center_pos); + float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52170,110 +49948,237 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputFiltersDialog { - +function Get-OBSRotatingSourceShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] -[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] +[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the spin_speed of OBSRotatingSourceShader +[Alias('spin_speed')] +[ComponentModel.DefaultBindingProperty('spin_speed')] +[Single] +$SpinSpeed, +# Set the rotation of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('rotation')] +[Single] +$Rotation, +# Set the zoomin of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('zoomin')] +[Single] +$Zoomin, +# Set the keep_aspectratio of OBSRotatingSourceShader +[Alias('keep_aspectratio')] +[ComponentModel.DefaultBindingProperty('keep_aspectratio')] +[Management.Automation.SwitchParameter] +$KeepAspectratio, +# Set the x_center of OBSRotatingSourceShader +[Alias('x_center')] +[ComponentModel.DefaultBindingProperty('x_center')] +[Single] +$XCenter, +# Set the y_center of OBSRotatingSourceShader +[Alias('y_center')] +[ComponentModel.DefaultBindingProperty('y_center')] +[Single] +$YCenter, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rotating-source' +$ShaderNoun = 'OBSRotatingSourceShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//spin speed higher the slower +uniform float spin_speed< + string label = "Spin Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float rotation< + string label = "Rotation"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.0; + float step = 0.1; +> = 0.0; +uniform float zoomin< + string label = "Zoom"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform bool keep_aspectratio = true; +uniform float x_center< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float y_center< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +//main fragment code +//from lioran to nutella with love +float4 mainImage(VertData v_in) : TARGET +{ + float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; + float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; + //get position on of the texture and focus on the middle + float i_rotation; + if (spin_speed == 0){ + //turn angle number into pi number + i_rotation = rotation/57.295779513; + }else{ + //use elapsed time for spinning if spin speed is not 0 + i_rotation = elapsed_time * spin_speed; + } + float2 i_point; + i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); + i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); + + //get the angle from center , returns pi number + float i_dir = atan(i_point.y/i_point.x); + if(i_point.x < 0.0){ + i_dir += 3.14159265359; + } + + //get the distance from the centers + float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); + //multiple distance by the zoomin value + i_distance *= zoomin; + + //shift the texture position based on angle and distance from the middle + i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; + i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; + + //draw normally from new point + return image.Sample(textureSampler, i_point); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52282,222 +50187,374 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputInteractDialog { - +function Get-OBSRotatoeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] -[Alias('obs.powershell.websocket.OpenInputInteractDialog')] +[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the ViewProj of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSRotatoeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRotatoeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRotatoeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRotatoeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSRotatoeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSRotatoeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed_percent of OBSRotatoeShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the Axis_X of OBSRotatoeShader +[Alias('Axis_X')] +[ComponentModel.DefaultBindingProperty('Axis_X')] +[Single] +$AxisX, +# Set the Axis_Y of OBSRotatoeShader +[Alias('Axis_Y')] +[ComponentModel.DefaultBindingProperty('Axis_Y')] +[Single] +$AxisY, +# Set the Axis_Z of OBSRotatoeShader +[Alias('Axis_Z')] +[ComponentModel.DefaultBindingProperty('Axis_Z')] +[Single] +$AxisZ, +# Set the Angle_Degrees of OBSRotatoeShader +[Alias('Angle_Degrees')] +[ComponentModel.DefaultBindingProperty('Angle_Degrees')] +[Single] +$AngleDegrees, +# Set the Rotate_Transform of OBSRotatoeShader +[Alias('Rotate_Transform')] +[ComponentModel.DefaultBindingProperty('Rotate_Transform')] +[Management.Automation.SwitchParameter] +$RotateTransform, +# Set the Rotate_Pixels of OBSRotatoeShader +[Alias('Rotate_Pixels')] +[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] +[Management.Automation.SwitchParameter] +$RotatePixels, +# Set the Rotate_Colors of OBSRotatoeShader +[Alias('Rotate_Colors')] +[ComponentModel.DefaultBindingProperty('Rotate_Colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the center_width_percentage of OBSRotatoeShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRotatoeShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# Set the notes of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rotatoe' +$ShaderNoun = 'OBSRotatoeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int speed_percent< + string label = "speed percentage"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 50; // +uniform float Axis_X< + string label = "Axis X"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.1; +> = 0.0; +uniform float Axis_Y< + string label = "Axis Y"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 0.0; +uniform float Axis_Z< + string label = "Axis Z"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; +uniform float Angle_Degrees< + string label = "Angle Degrees"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 0.01; +> = 45.0; +uniform bool Rotate_Transform = true; +uniform bool Rotate_Pixels = false; +uniform bool Rotate_Colors = false; +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform string notes< + string widget_type = "info"; +> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float3x3 rotAxis(float3 axis, float a) { + float s=sin(a); + float c=cos(a); + float oc=1.0-c; + float3 as=axis*s; + + float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); + float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); + return p*oc+q; } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); -} + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputPropertiesDialog { + // use matrix to transform rotation + if (Rotate_Transform) + vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; + vert_out.uv = v_in.uv * uv_scale + uv_offset; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] -[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] -param( + return vert_out; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // use matrix to transform pixels + if (Rotate_Pixels) + { + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); + } + if (Rotate_Colors) + rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; -process { + return rgba; +} +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52506,120 +50563,330 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSSourceProjector { - +function Get-OBSRoundedRect2Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] -[Alias('obs.powershell.websocket.OpenSourceProjector')] +[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] param( - +# Set the corner_radius of OBSRoundedRect2Shader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedRect2Shader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRect2Shader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRect2Shader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRect2Shader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRect2Shader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# Set the faster_scan of OBSRoundedRect2Shader +[Alias('faster_scan')] +[ComponentModel.DefaultBindingProperty('faster_scan')] +[Management.Automation.SwitchParameter] +$FasterScan, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('projectorGeometry')] -[string] -$ProjectorGeometry, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect2' +$ShaderNoun = 'OBSRoundedRect2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float border_alpha_end< + string label = "Border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float alpha_cut_off< + string label = "Aplha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; +uniform bool faster_scan = true; +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + float check_dist = float(corner_radius); + if(border_thickness > check_dist) + check_dist = border_thickness; + if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(-check_dist); + } + if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(-check_dist); + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(!faster_scan || closedEdgeX != 0){ + [loop] for(int x = 1;float(x) check_dist && border_thickness > corner_radius){ + if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ + float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(floor(cd) > corner_radius) + return float4(0.0,0.0,0.0,0.0); + if(cd > corner_radius){ + d = border_thickness + cd - corner_radius; + } else if(d > border_thickness){ + d = border_thickness; + } + }else if(d > border_thickness){ + d = border_thickness; + } + } + if(floor(d) <= check_dist){ + if(border_thickness > 0){ + if(ceil(check_dist-d) <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + if(border_alpha_start < border_alpha_end){ + fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; + fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); + } + if(d > check_dist) + fade_color.a *= 1.0 -(d - check_dist); + return fade_color; + }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ + float4 fade_color = border_color; + float f; + if(border_thickness > (check_dist-d)) + f = border_thickness - (check_dist-d); + else + f = 1.0 -((check_dist-d) - border_thickness); + fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; + return fade_color; + } + } + if (d > check_dist) + pixel.a *= 1.0 - (d - check_dist); + return pixel; + + } + return float4(0.0,0.0,0.0,0.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52628,115 +50895,354 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSVideoMixProjector { - +function Get-OBSRoundedRectPerCornerShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] -[Alias('obs.powershell.websocket.OpenVideoMixProjector')] +[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('videoMixType')] -[string] -$VideoMixType, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, - +# Set the corner_radius_tl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tl')] +[ComponentModel.DefaultBindingProperty('corner_radius_tl')] +[Int32] +$CornerRadiusTl, +# Set the corner_radius_tr of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tr')] +[ComponentModel.DefaultBindingProperty('corner_radius_tr')] +[Int32] +$CornerRadiusTr, +# Set the corner_radius_br of OBSRoundedRectPerCornerShader +[Alias('corner_radius_br')] +[ComponentModel.DefaultBindingProperty('corner_radius_br')] +[Int32] +$CornerRadiusBr, +# Set the corner_radius_bl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_bl')] +[ComponentModel.DefaultBindingProperty('corner_radius_bl')] +[Int32] +$CornerRadiusBl, +# Set the border_thickness of OBSRoundedRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('projectorGeometry')] -[string] -$ProjectorGeometry, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect_per_corner' +$ShaderNoun = 'OBSRoundedRectPerCornerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_radius_tl< + string label = "Corner radius top left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_tr< + string label = "Corner radius top right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_br< + string label = "Corner radius bottom right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_bl< + string label = "Corner radius bottom left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; + int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; + int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; + int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; + + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_radius_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_radius_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_radius_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(dAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52745,110 +51251,331 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSInput { - +function Get-OBSRoundedRectPerSideShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] -[Alias('obs.powershell.websocket.RemoveInput')] +[Alias('Set-OBSRoundedRectPerSideShader','Add-OBSRoundedRectPerSideShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the corner_radius_bottom of OBSRoundedRectPerSideShader +[Alias('corner_radius_bottom')] +[ComponentModel.DefaultBindingProperty('corner_radius_bottom')] +[Int32] +$CornerRadiusBottom, +# Set the corner_radius_left of OBSRoundedRectPerSideShader +[Alias('corner_radius_left')] +[ComponentModel.DefaultBindingProperty('corner_radius_left')] +[Int32] +$CornerRadiusLeft, +# Set the corner_radius_top of OBSRoundedRectPerSideShader +[Alias('corner_radius_top')] +[ComponentModel.DefaultBindingProperty('corner_radius_top')] +[Int32] +$CornerRadiusTop, +# Set the corner_radius_right of OBSRoundedRectPerSideShader +[Alias('corner_radius_right')] +[ComponentModel.DefaultBindingProperty('corner_radius_right')] +[Int32] +$CornerRadiusRight, +# Set the border_thickness of OBSRoundedRectPerSideShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectPerSideShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRectPerSideShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRectPerSideShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRectPerSideShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect_per_side' +$ShaderNoun = 'OBSRoundedRectPerSideShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int corner_radius_bottom< + string label = "Corner radius bottom"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_left< + string label = "Corner radius left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_top< + string label = "Corner radius top"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_right< + string label = "Corner radius right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float4 output_color = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(output_color.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return output_color; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x corner_radius){ + closedEdgeXabs = 0; + } + if(closedEdgeYabs > corner_radius){ + closedEdgeYabs = 0; + } + if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ + return output_color; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float closest = closedEdgeXabsAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52857,105 +51584,183 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSProfile { - +function Get-OBSRoundedRectShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] -[Alias('obs.powershell.websocket.RemoveProfile')] +[Alias('Set-OBSRoundedRectShader','Add-OBSRoundedRectShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the corner_radius of OBSRoundedRectShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedRectShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect' +$ShaderNoun = 'OBSRoundedRectShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); + float4 output_color = image.Sample(textureSampler, v_in.uv); + + float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); + float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); + + bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; + bool is_within_radius = pixel_distance_from_center <= corner_radius; + + bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); + bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); + + return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52964,228 +51769,365 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSScene { - +function Get-OBSRoundedStrokeGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] -[Alias('obs.powershell.websocket.RemoveScene')] +[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the corner_radius of OBSRoundedStrokeGradientShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeGradientShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the rotation_speed of OBSRoundedStrokeGradientShader +[Alias('rotation_speed')] +[ComponentModel.DefaultBindingProperty('rotation_speed')] +[Int32] +$RotationSpeed, +# Set the border_colorL of OBSRoundedStrokeGradientShader +[Alias('border_colorL')] +[ComponentModel.DefaultBindingProperty('border_colorL')] +[String] +$BorderColorL, +# Set the border_colorR of OBSRoundedStrokeGradientShader +[Alias('border_colorR')] +[ComponentModel.DefaultBindingProperty('border_colorR')] +[String] +$BorderColorR, +# Set the center_width of OBSRoundedStrokeGradientShader +[Alias('center_width')] +[ComponentModel.DefaultBindingProperty('center_width')] +[Int32] +$CenterWidth, +# Set the center_height of OBSRoundedStrokeGradientShader +[Alias('center_height')] +[ComponentModel.DefaultBindingProperty('center_height')] +[Int32] +$CenterHeight, +# Set the notes of OBSRoundedStrokeGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_stroke_gradient' +$ShaderNoun = 'OBSRoundedStrokeGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int rotation_speed< + string label = "rotation speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_colorL; +uniform float4 border_colorR; +//uniform float color_spread = 2.0; +uniform int center_width< + string label = "center width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height< + string label = "center height"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; +// float3 hsv2rgb(float3 c) +// { +// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); +// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); +// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +// } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float mod(float x, float y) +{ + return x - y * floor(x/y); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 gradient(float c) { + c = mod(c , 2.0); + if(c < 0.0f){ + c = c * -1.0; + } + if(c > 1.0){ + c = 1.0 - c; + if(c < 0.0f){ + c = c + 1.0; } + } + return lerp(border_colorL, border_colorR, c); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 getBorderColor(float2 toCenter){ + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; + return gradient((angle / 3.14159265f) + angleMod); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 st = v_in.uv * uv_scale; + float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); + float2 toCenter = center_pixel_coordinates - st; + + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return output_color; + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return getBorderColor(toCenter); } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + else + { + return output_color; + } + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; } + } + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; + } + } + return float4(0.0, 0.0, 0.0, 0.0); } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSceneItem { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] -[Alias('obs.powershell.websocket.RemoveSceneItem')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53194,319 +52136,287 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSourceFilter { - +function Get-OBSRoundedStrokeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] -[Alias('obs.powershell.websocket.RemoveSourceFilter')] +[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] param( - +# Set the corner_radius of OBSRoundedStrokeShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the border_color of OBSRoundedStrokeShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the notes of OBSRoundedStrokeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_stroke' +$ShaderNoun = 'OBSRoundedStrokeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float4 border_color; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return float4(output_color); + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return border_color; } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + else + { + return float4(output_color); } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Resume-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] -[Alias('obs.powershell.websocket.ResumeRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return border_color; } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + else + { + return float4(output_color); } + } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return border_color; } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + else + { + return output_color; } - - if ($PassThru) { - [PSCustomObject]$requestPayload + } + return float4(0.0, 0.0, 0.0, 0.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Save-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] -[Alias('obs.powershell.websocket.SaveReplayBuffer')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53515,146 +52425,266 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Save-OBSSourceScreenshot { - +function Get-OBSScanLineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] -[Alias('obs.powershell.websocket.SaveSourceScreenshot')] +[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] param( - +# Set the lengthwise of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('lengthwise')] +[Management.Automation.SwitchParameter] +$Lengthwise, +# Set the animate of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the speed of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the angle of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the shift of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('shift')] +[Management.Automation.SwitchParameter] +$Shift, +# Set the boost of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('boost')] +[Management.Automation.SwitchParameter] +$Boost, +# Set the floor of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('floor')] +[Single] +$Floor, +# Set the period of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('period')] +[Single] +$Period, +# Set the notes of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFilePath')] -[string] -$ImageFilePath, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'scan_line' +$ShaderNoun = 'OBSScanLineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Scan Line Effect for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +//Count the number of scanlines we want via height or width, adjusts the sin wave period +uniform bool lengthwise; +//Do we want the scanlines to move? +uniform bool animate; +//How fast do we want those scanlines to move? +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10000.0; + float step = 1; +> = 1000; +//What angle should the scanlines come in at (based in degrees) +uniform float angle< + string label = "angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 45; +//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit +//By default values for color range from 0 to 1 +//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen +uniform bool shift = true; +uniform bool boost = true; +//Increases the minimum value of the sin wave +uniform float floor< + string label = "Floor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 0.0; +//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input +uniform float period< + string label = "Period"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 1000.0; + float step = 1.0; +> = 10.0; +uniform string notes< + string widget_type = "info"; +> = "floor affects the minimum opacity of the scan line"; +float4 mainImage(VertData v_in) : TARGET +{ + //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 + // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 + float nfloor = clamp(floor, 0.0, 100.0) * 0.01; + float nperiod = max(period, 1.0); + float gap = 1 - nfloor; + float pi = 3.1415926535897932384626; + float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); + float nspeed = 0.0; + if(animate){ + nspeed = speed * 0.0001; + } + + float4 color = image.Sample(textureSampler, v_in.uv); + + float t = elapsed_time * nspeed; + + if(!lengthwise){ + float base_height = 1.0 / uv_pixel_interval.y; + float h_interval = pi * base_height; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } + else{ + float base_width = 1.0 / uv_pixel_interval.x; + float w_interval = pi * base_width; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - - Get-Item $paramCopy["imageFilePath"] | - Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | - Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru - } @@ -53662,223 +52692,449 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSCallVendorRequest { - +function Get-OBSSeascapeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] -[Alias('obs.powershell.websocket.CallVendorRequest')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('vendorName')] -[string] -$VendorName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestType')] -[string] -$RequestType, - +# Set the AA of OBSSeascapeShader +[ComponentModel.DefaultBindingProperty('AA')] +[Management.Automation.SwitchParameter] +$AA, +# Set the SEA_HEIGHT of OBSSeascapeShader +[Alias('SEA_HEIGHT')] +[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] +[Single] +$SEAHEIGHT, +# Set the SEA_CHOPPY of OBSSeascapeShader +[Alias('SEA_CHOPPY')] +[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] +[Single] +$SEACHOPPY, +# Set the SEA_SPEED of OBSSeascapeShader +[Alias('SEA_SPEED')] +[ComponentModel.DefaultBindingProperty('SEA_SPEED')] +[Single] +$SEASPEED, +# Set the SEA_FREQ of OBSSeascapeShader +[Alias('SEA_FREQ')] +[ComponentModel.DefaultBindingProperty('SEA_FREQ')] +[Single] +$SEAFREQ, +# Set the SEA_BASE of OBSSeascapeShader +[Alias('SEA_BASE')] +[ComponentModel.DefaultBindingProperty('SEA_BASE')] +[String] +$SEABASE, +# Set the SEA_WATER_COLOR of OBSSeascapeShader +[Alias('SEA_WATER_COLOR')] +[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] +[String] +$SEAWATERCOLOR, +# Set the CAMERA_SPEED of OBSSeascapeShader +[Alias('CAMERA_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] +[Single] +$CAMERASPEED, +# Set the CAMERA_TURN_SPEED of OBSSeascapeShader +[Alias('CAMERA_TURN_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] +[Single] +$CAMERATURNSPEED, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestData')] -[PSObject] -$RequestData, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'seascape' +$ShaderNoun = 'OBSSeascapeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +/* + * "Seascape" by Alexander Alekseev aka TDM - 2014 + * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + * Contact: tdmaav@gmail.com + * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro + */ +#define NUM_STEPS 8 +#define PI 3.141592 +#define EPSILON 0.001 +uniform bool AA< + string label = "Smooth (more resources)"; +> = false; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +#ifndef OPENGL +#define mat2 float2x2 +#define mat3 float3x3 +#define fract frac +#define mix lerp +#endif - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// sea +#define ITER_GEOMETRY 3 +#define ITER_FRAGMENT 5 +uniform float SEA_HEIGHT< + string label = "Sea Height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.5; + float step = 0.001; +> = 0.6; +uniform float SEA_CHOPPY< + string label = "Sea Choppy"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 4.0; +uniform float SEA_SPEED< + string label = "Sea Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.8; +uniform float SEA_FREQ< + string label = "Sea Frequency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.16; +uniform float4 SEA_BASE< + string label = "Sea Base"; +> = {0.0,0.09,0.18,1.0}; +uniform float4 SEA_WATER_COLOR< + string label = "Sea Water"; +> = {0.48,0.54,0.36,1.0}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float CAMERA_SPEED< + string label = "Camera Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float CAMERA_TURN_SPEED< + string label = "Camera Turn Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float SEA_TIME(){ + return 1.0 + elapsed_time * SEA_SPEED; } +// math +mat3 fromEuler(float3 ang) { + float2 a1 = float2(sin(ang.x),cos(ang.x)); + float2 a2 = float2(sin(ang.y),cos(ang.y)); + float2 a3 = float2(sin(ang.z),cos(ang.z)); + return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), + float3(-a2.y*a1.x,a1.y*a2.y,a2.x), + float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); +} -} +float hash(float2 p) { + float h = dot(p,float2(127.1,311.7)); + return fract(sin(h)*43758.5453123); +} - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSCustomEvent { +float noise(float2 p) { + float2 i = floor( p ); + float2 f = fract( p ); + float2 u = f*f*(3.0-2.0*f); + return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), + hash( i + float2(1.0,0.0) ), u.x), + mix( hash( i + float2(0.0,1.0) ), + hash( i + float2(1.0,1.0) ), u.x), u.y); +} +// lighting +float diffuse(float3 n,float3 l,float p) { + return pow(dot(n,l) * 0.4 + 0.6,p); +} +float specular(float3 n,float3 l,float3 e,float s) { + float nrm = (s + 8.0) / (PI * 8.0); + return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] -[Alias('obs.powershell.websocket.BroadcastCustomEvent')] -param( +// sky +float3 getSkyColor(float3 e) { + e.y = (max(e.y,0.0)*0.8+0.2)*0.8; + return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('eventData')] -[PSObject] -$EventData, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// sea +float sea_octave(float2 uv, float choppy) { + uv += noise(uv); + float2 wv = 1.0-abs(sin(uv)); + float2 swv = abs(cos(uv)); + wv = mix(wv,swv,wv); + return pow(1.0-pow(wv.x * wv.y,0.65),choppy); +} +float map(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; + uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); -process { + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_GEOMETRY; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} +float map_detailed(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_FRAGMENT; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { + float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); + fresnel = min(pow(fresnel,3.0), 0.5); + + float3 reflected = getSkyColor(reflect(eye,n)); + float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; + + float3 color = mix(refracted,reflected,fresnel); + + float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); + color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; + + float s = specular(n,l,eye,60.0); + color += float3(s,s,s); + + return color; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +// tracing +float3 getNormal(float3 p, float eps) { + float3 n; + n.y = map_detailed(p); + n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; + n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; + n.y = eps; + return normalize(n); +} + +float heightMapTracing(float3 ori, float3 dir, out float3 p) { + float tm = 0.0; + float tx = 1000.0; + float hx = map(ori + dir * tx); + if(hx > 0.0) { + p = ori + dir * tx; + return tx; + } + float hm = map(ori + dir * tm); + float tmid = 0.0; + for(int i = 0; i < NUM_STEPS; i++) { + tmid = mix(tm,tx, hm/(hm-hx)); + p = ori + dir * tmid; + float hmid = map(p); + if(hmid < 0.0) { + tx = tmid; + hx = hmid; + } else { + tm = tmid; + hm = hmid; } + } + return tmid; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float3 getPixel(in float2 coord, float time) { + float2 uv = coord / uv_size.xy; + uv = uv * 2.0 - 1.0; + uv.x *= uv_size.x / uv_size.y; + + // ray + float3 ang = float3(sin(time*3.0*CAMERA_TURN_SPEED)*0.1,sin(time*CAMERA_TURN_SPEED)*0.2+0.3,time*CAMERA_TURN_SPEED); + float3 ori = float3(0.0,3.5,time*5.0*CAMERA_SPEED); + float3 dir = normalize(float3(uv.xy,-2.0)); + dir.z += length(uv) * 0.14; + dir = mul(normalize(dir), fromEuler(ang)); + + // tracing + float3 p; + heightMapTracing(ori,dir,p); + float3 dist = p - ori; + float3 n = getNormal(p, dot(dist,dist) * (0.1 / uv_size.x)); + float3 light = normalize(float3(0.0,1.0,0.8)); + + // color + return mix( + getSkyColor(dir), + getSeaColor(p,n,light,dir,dist), + pow(smoothstep(0.0,-0.02,dir.y),0.2)); +} + +// main +float4 mainImage(VertData v_in) : TARGET +{ + float time = elapsed_time * 0.3; + float2 fragCoord = float2(v_in.uv.x * uv_size.x, (1.0 - v_in.uv.y) * uv_size.y); + + float3 color = float3(0.0,0.0,0.0);; + if (AA){ + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + float2 uv = fragCoord+float2(i,j)/3.0; + color += getPixel(uv, time); } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + color /= 9.0; + }else{ + color = getPixel(fragCoord, time); + } + + // post + return float4(pow(color,float3(0.65,0.65,0.65)), 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53887,115 +53143,194 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSOffsetMediaInputCursor { - +function Get-OBSSeasickShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] -[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the notes of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the amplitude of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the speed of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the frequency of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('frequency')] +[Single] +$Frequency, +# Set the opacity of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] -[double] -$MediaCursorOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'seasick' +$ShaderNoun = 'OBSSeasickShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Seasick - an effect for OBS Studio +// +uniform string notes< + string widget_type = "info"; +> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; +uniform float amplitude< + string label = "amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.03; +uniform float speed< + string label = "speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float frequency< + string label = "frequency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 6.0; +uniform float opacity< + string label = "opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); + float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); + return image.Sample(textureSampler, coord) * opacity; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54004,331 +53339,305 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSPauseRecord { - +function Get-OBSSelectiveColorShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] -[Alias('obs.powershell.websocket.PauseRecord')] +[Alias('Set-OBSSelectiveColorShader','Add-OBSSelectiveColorShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the cutoff_Red of OBSSelectiveColorShader +[Alias('cutoff_Red')] +[ComponentModel.DefaultBindingProperty('cutoff_Red')] +[Single] +$CutoffRed, +# Set the cutoff_Green of OBSSelectiveColorShader +[Alias('cutoff_Green')] +[ComponentModel.DefaultBindingProperty('cutoff_Green')] +[Single] +$CutoffGreen, +# Set the cutoff_Blue of OBSSelectiveColorShader +[Alias('cutoff_Blue')] +[ComponentModel.DefaultBindingProperty('cutoff_Blue')] +[Single] +$CutoffBlue, +# Set the cutoff_Yellow of OBSSelectiveColorShader +[Alias('cutoff_Yellow')] +[ComponentModel.DefaultBindingProperty('cutoff_Yellow')] +[Single] +$CutoffYellow, +# Set the acceptance_Amplifier of OBSSelectiveColorShader +[Alias('acceptance_Amplifier')] +[ComponentModel.DefaultBindingProperty('acceptance_Amplifier')] +[Single] +$AcceptanceAmplifier, +# Set the show_Red of OBSSelectiveColorShader +[Alias('show_Red')] +[ComponentModel.DefaultBindingProperty('show_Red')] +[Management.Automation.SwitchParameter] +$ShowRed, +# Set the show_Green of OBSSelectiveColorShader +[Alias('show_Green')] +[ComponentModel.DefaultBindingProperty('show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the show_Blue of OBSSelectiveColorShader +[Alias('show_Blue')] +[ComponentModel.DefaultBindingProperty('show_Blue')] +[Management.Automation.SwitchParameter] +$ShowBlue, +# Set the show_Yellow of OBSSelectiveColorShader +[Alias('show_Yellow')] +[ComponentModel.DefaultBindingProperty('show_Yellow')] +[Management.Automation.SwitchParameter] +$ShowYellow, +# Set the notes of OBSSelectiveColorShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the background_type of OBSSelectiveColorShader +[Alias('background_type')] +[ComponentModel.DefaultBindingProperty('background_type')] +[Int32] +$BackgroundType, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'selective_color' +$ShaderNoun = 'OBSSelectiveColorShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Selective Color shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//updated 4/13/2020: take into account the opacity/alpha of input image -thanks Skeletonbow for suggestion +//Converted to OpenGL by Q-mii February 25, 2020 +uniform float cutoff_Red< + string label = "cutoff Red"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.40; +uniform float cutoff_Green< + string label = "cutoff Green"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.025; +uniform float cutoff_Blue< + string label = "cutoff Blue"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.25; +uniform float cutoff_Yellow< + string label = "cutoff Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.25; +uniform float acceptance_Amplifier< + string label = "acceptance Amplifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 5.0; +uniform bool show_Red = true; +uniform bool show_Green = true; +uniform bool show_Blue = true; +uniform bool show_Yellow = true; +uniform string notes< + string widget_type = "info"; +> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; +uniform int background_type< + string label = "background type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Grey"; + int option_1_value = 1; + string option_1_label = "Luma"; + int option_2_value = 2; + string option_2_label = "White"; + int option_3_value = 3; + string option_3_label = "Black"; + int option_4_value = 4; + string option_4_label = "Transparent"; + int option_5_value = 5; + string option_5_label = "Background Color"; +> = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSPressInputPropertiesButton { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] -[Alias('obs.powershell.websocket.PressInputPropertiesButton')] -param( +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.1415926535897932384626433832795;//acos(-1); + const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); + float4 color = image.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + float luminance = dot(coefLuma, color.rgb); + float4 gray = float4(luminance, luminance, luminance, 1.0); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + if (background_type == 0) + { + luminance = (color.r + color.g + color.b) * 0.3333; + gray = float4(luminance,luminance,luminance, 1.0); + } + //if (background_type == 1) + //{ + // gray = float4(luminance,luminance,luminance, 1.0); + //} + if (background_type == 2) + gray = float4(1.0,1.0,1.0,1.0); + if (background_type == 3) + gray = float4(0.0,0.0,0.0,1.0); + if (background_type == 4) + gray.a = 0.01; + if (background_type == 5) + gray = color; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); + float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); + float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); + + float rgLuminance = (color.r*1.4 + color.g*0.6)/2; + float rgDiff = abs(color.r-color.g)*1.4; + float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; -process { + float4 accept; + accept.r = float(show_Red) * (redness - cutoff_Red); + accept.g = float(show_Green) * (greenness - cutoff_Green); + accept.b = float(show_Blue) * (blueness - cutoff_Blue); + accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); + float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); + float modAcceptance = min (acceptance * acceptance_Amplifier, 1); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 result = color; + if (result.a > 0) { + result = modAcceptance * color + (1.0 - modAcceptance) * gray; + //result.a *= gray.a; + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + return result; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSSleep { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] -[Alias('obs.powershell.websocket.Sleep')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepMillis')] -[ValidateRange(0,50000)] -[double] -$SleepMillis, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepFrames')] -[ValidateRange(0,10000)] -[double] -$SleepFrames, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54337,237 +53646,346 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSStreamCaption { - +function Get-OBSShakeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] -[Alias('obs.powershell.websocket.SendStreamCaption')] +[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('captionText')] -[string] -$CaptionText, -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSShakeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSShakeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSShakeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSShakeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSShakeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSShakeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSShakeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSShakeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the local_time of OBSShakeShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the random_scale of OBSShakeShader +[Alias('random_scale')] +[ComponentModel.DefaultBindingProperty('random_scale')] +[Single] +$RandomScale, +# Set the worble of OBSShakeShader +[ComponentModel.DefaultBindingProperty('worble')] +[Management.Automation.SwitchParameter] +$Worble, +# Set the speed of OBSShakeShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the min_growth_pixels of OBSShakeShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] +[Single] +$MinGrowthPixels, +# Set the max_growth_pixels of OBSShakeShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, +# Set the randomize_movement of OBSShakeShader +[Alias('randomize_movement')] +[ComponentModel.DefaultBindingProperty('randomize_movement')] +[Management.Automation.SwitchParameter] +$RandomizeMovement, +# Set the notes of OBSShakeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } -} +process { +$shaderName = 'shake' +$ShaderNoun = 'OBSShakeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +// Added some randomization based upon random_scale input +// updated for latest version of obs-shaderfilter +uniform float4x4 ViewProj; +uniform texture2d image; -} +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform float local_time; - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByKeySequence { +uniform float random_scale< + string label = "random scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.25; +uniform bool worble = false; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = -2.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform bool randomize_movement = false; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] -[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] -param( +uniform string notes< + string widget_type = "info"; +> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyId')] -[string] -$KeyId, +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers')] -[PSObject] -$KeyModifiers, +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] -[switch] -$KeyModifiersshift, +//noise values in range if 0.0 to 1.0 -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.control')] -[switch] -$KeyModifierscontrol, +float noise3D(float x, float y, float z) { + float ptr = 0.0f; + return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] -[switch] -$KeyModifiersalt, +VertData mainTransform(VertData v_in) +{ + VertData vert_out; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.command')] -[switch] -$KeyModifierscommand, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float3 pos = v_in.pos.xyz; + float t; + float s; + float noise; + if (randomize_movement) + { + t = (rand_f * 2) - 1.0f; + s = (1 - rand_f * 2) - 1.0f;; + noise = clamp( rand_f * random_scale,-0.99, 0.99); + } + else + { + t = (1 + sin(elapsed_time * speed)) / 2; + s = (1 + cos(elapsed_time * speed)) / 2; + noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); + } + float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); + float3 min_pos; + float3 max_pos; + if (worble) + { + min_pos = pos + direction_from_center * min_growth_pixels * 0.5; + max_pos = pos + direction_from_center * max_growth_pixels * 0.5; + } + else + { + min_pos = pos + direction_from_center * 0.5; + max_pos = min_pos; + } -process { + float3 current_pos = min_pos * (1 - t) + max_pos * t; + //current_pos.x = v_in.pos.x + (t * min_pos.x); + current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); + //current_pos.y = v_in.pos.y + (s * min_pos.y); + //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; + float2 offset = uv_offset; + offset.x = uv_offset.x * (1 - t + noise); + offset.y = uv_offset.y * (1 - s + noise); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + + //float2 scale = uv_scale; + //scale += dot(pos - current_pos, 1); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54576,227 +53994,371 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByName { - +function Get-OBSShineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] -[Alias('obs.powershell.websocket.TriggerHotkeyByName')] +[Alias('Set-OBSShineShader','Add-OBSShineShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('hotkeyName')] -[string] -$HotkeyName, - +# Set the l_tex of OBSShineShader +[Alias('l_tex')] +[ComponentModel.DefaultBindingProperty('l_tex')] +[String] +$LTex, +# Set the shine_color of OBSShineShader +[Alias('shine_color')] +[ComponentModel.DefaultBindingProperty('shine_color')] +[String] +$ShineColor, +# Set the speed_percent of OBSShineShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the gradient_percent of OBSShineShader +[Alias('gradient_percent')] +[ComponentModel.DefaultBindingProperty('gradient_percent')] +[Int32] +$GradientPercent, +# Set the delay_percent of OBSShineShader +[Alias('delay_percent')] +[ComponentModel.DefaultBindingProperty('delay_percent')] +[Int32] +$DelayPercent, +# Set the Apply_To_Alpha_Layer of OBSShineShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the ease of OBSShineShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the hide of OBSShineShader +[ComponentModel.DefaultBindingProperty('hide')] +[Management.Automation.SwitchParameter] +$Hide, +# Set the reverse of OBSShineShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# Set the One_Direction of OBSShineShader +[Alias('One_Direction')] +[ComponentModel.DefaultBindingProperty('One_Direction')] +[Management.Automation.SwitchParameter] +$OneDirection, +# Set the glitch of OBSShineShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSShineShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the start_adjust of OBSShineShader +[Alias('start_adjust')] +[ComponentModel.DefaultBindingProperty('start_adjust')] +[Single] +$StartAdjust, +# Set the stop_adjust of OBSShineShader +[Alias('stop_adjust')] +[ComponentModel.DefaultBindingProperty('stop_adjust')] +[Single] +$StopAdjust, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('contextName')] -[string] -$ContextName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'shine' +$ShaderNoun = 'OBSShineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +// use color to control shine amount, use transition wipes or make your own alpha texture +// slerp not currently used, for circular effects +//Converted to OpenGL by Exeldro February 14, 2022 +uniform texture2d l_tex; +uniform float4 shine_color ; +uniform int speed_percent< + string label = "speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 25; +uniform int gradient_percent< + string label = "gradient percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; +uniform int delay_percent< + string label = "delay percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool ease = false; +uniform bool hide = false; +uniform bool reverse = false; +uniform bool One_Direction = true; +uniform bool glitch = false; +uniform string notes< + string widget_type = "info"; +> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; +uniform float start_adjust; +uniform float stop_adjust; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; +} + +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} + +float4 slerp(float4 start, float4 end, float percent) +{ + // Dot product - the cosine of the angle between 2 vectors. + float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; + // Clamp it to be in the range of Acos() + // This may be unnecessary, but floating point + // precision can be a fickle mistress. + dotf = clamp(dotf, -1.0f, 1.0f); + // Acos(dot) returns the angle between start and end, + // And multiplying that by percent returns the angle between + // start and the final result. + float theta = acos(dotf)*percent; + float4 RelativeVec = normalize(end - start * dotf); + + // Orthonormal basis + // The final result. + return ((start*cos(theta)) + (RelativeVec*sin(theta))); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + // convert input for vector math + float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); + float speed = speed_percent * 0.01; + float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); + float delay = clamp(delay_percent * 0.01, 0.0, 1.0); + - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + // circular easing variable + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = abs(sin(elapsed_time * speed)); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + // if time is greater than direction, we are going up! + direction = t - direction; -} + // split into segments with frac or mod. + // delay is the gap between starting and ending of the sine wave, use speed to compensate + t = (frac(t) - delay) * (1 / (1 - delay)); + t = 1 + max(t,0.0); + float s = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration -} + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerMediaInputAction { + //if Unidirectional disable on return + if (One_Direction && (direction < 0.0)) + { + s = 0; + } + else + { + s = Styler(t, 0, c, d, ease); + } + // combine luma texture and user defined shine color + float luma = l_tex.Sample(textureSampler, v_in.uv).x; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] -[Alias('obs.powershell.websocket.TriggerMediaInputAction')] -param( + // - adjust for min and max + if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) + { -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + if (reverse) + { + luma = 1.0 - luma; + } + + // user color with luma + float4 output_color = float4(shine_color.rgb, luma); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaAction')] -[string] -$MediaAction, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // use luma texture to add alpha and shine + // if behind glow, consider trailing gradient shine then show underlying image + if (luma <= time - softness) + { + float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); + if (Apply_To_Alpha_Layer) + alpha_behind *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_behind); + } -process { + // if in front of glow, consider if the underlying image is hidden + if (luma >= time) + { + // if hide, make the transition better + if (hide) + { + return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); + } + else + { + return rgba; + } + } + // else show the glow area, with luminance + float alpha_ = (time - luma) / softness; + if (Apply_To_Alpha_Layer) + alpha_ *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_); + } + else + { + return rgba; + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54805,319 +54367,288 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerStudioModeTransition { - +function Get-OBSSimpleGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] -[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] +[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed_percentage of OBSSimpleGradientShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSSimpleGradientShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Lens_Flair of OBSSimpleGradientShader +[Alias('Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Lens_Flair')] +[Management.Automation.SwitchParameter] +$LensFlair, +# Set the Animate_Lens_Flair of OBSSimpleGradientShader +[Alias('Animate_Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] +[Management.Automation.SwitchParameter] +$AnimateLensFlair, +# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSSimpleGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSSimpleGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSSimpleGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'simple_gradient' +$ShaderNoun = 'OBSSimpleGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +//lots of room to play here +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform int speed_percentage< + string label = "speed percentage"; + string widget_type = "slider"; + int minimum = -500; + int maximum = 500; + int step = 1; +> = 240; // +uniform int alpha_percentage< + string label = "aplha percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform bool Lens_Flair = false; +uniform bool Animate_Lens_Flair = false; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} +float4 mainImage(VertData v_in) : TARGET +{ + float4 background_color = image.Sample(textureSampler, v_in.uv); + int no_colors = 4; + float3 colors[4]; + colors[0] = float3(1.0,0.0,0.0); + colors[1] = float3(0.0,1.0,0.0); + colors[2] = float3(0.0,0.0,1.0); + colors[3] = float3(1.0,1.0,1.0); + float alpha = float(alpha_percentage) * 0.01; + float speed = float(speed_percentage) * 0.01; -} + float mx = max(uv_size.x , uv_size.y); + //float2 uv = v_in.uv / mx; + float3 rgb = background_color.rgb; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentPreviewScene { + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(background_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + rgb = float3(v_in.uv.x, v_in.uv.y, 0.10 + 0.85 * sin(elapsed_time * speed)); + } + //create lens flare like effect + if (Lens_Flair) + { + float2 lens_flair_coordinates = float2(0.95 ,0.95); + if (Animate_Lens_Flair) + lens_flair_coordinates *= float2(sin(elapsed_time * speed) ,cos(elapsed_time * speed)); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] -param( + float dist = distance(v_in.uv, ( lens_flair_coordinates * uv_scale + uv_offset)); + for (int i = 0; i < no_colors; ++i) { + rgb += lerp(rgb, colors[i], dist * 1.5) * 0.25; + } + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //float3 col = colors[0]; +/* for (int i = 1; i < no_colors; ++i) { + float3 hole = float3( + sin(1.5 - distance(v_in.uv.x / mx, colors[i].x / mx) * 2.5 * speed), + sin(1.5 - distance(v_in.uv.y / mx, colors[i].y / mx) * 2.5 * speed), + colors[i].z); + rgb = lerp(rgb, hole, 0.1); +*/ +/* float3 hole = lerp(colors[i-1], colors[i], sin(elapsed_time * speed)); + col = lerp(col, hole, v_in.uv.x); +*/ + //} +// rgb = fflerp(rgb, col, 0.5); -process { + //try prepositioned colors with colors[] array timing + //creates an animated color spotlight +/* int color_index = int(sin(elapsed_time * speed) * no_colors); + float3 start_color = colors[color_index]; + float3 end_color; + if (color_index >= 0) + { + end_color = colors[color_index - 1]; + } + else + { + end_color = colors[no_colors - 1]; + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); +*/ + float4 rgba; + if (Apply_To_Alpha_Layer == false) + { + rgba = lerp(background_color,float4(rgb, 1.0),alpha); + } + else + { + rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); + } + if (Apply_To_Specific_Color) + { + float4 original_color = background_color; + background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; + rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); + } + return rgba; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProfile { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] -[Alias('obs.powershell.websocket.SetCurrentProfile')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55126,324 +54657,370 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProgramScene { - +function Get-OBSSimplexNoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] -[Alias('obs.powershell.websocket.SetCurrentProgramScene')] +[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Snap_Percent of OBSSimplexNoiseShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] +[Single] +$SnapPercent, +# Set the Speed_Percent of OBSSimplexNoiseShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Single] +$SpeedPercent, +# Set the Resolution of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Resolution')] +[Single] +$Resolution, +# Set the Fractal of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Fractal')] +[Management.Automation.SwitchParameter] +$Fractal, +# Set the Use_Alpha_Layer of OBSSimplexNoiseShader +[Alias('Use_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$UseAlphaLayer, +# Set the Fore_Color of OBSSimplexNoiseShader +[Alias('Fore_Color')] +[ComponentModel.DefaultBindingProperty('Fore_Color')] +[String] +$ForeColor, +# Set the Back_Color of OBSSimplexNoiseShader +[Alias('Back_Color')] +[ComponentModel.DefaultBindingProperty('Back_Color')] +[String] +$BackColor, +# Set the Alpha_Percent of OBSSimplexNoiseShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Notes of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'simplex_noise' +$ShaderNoun = 'OBSSimplexNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +//based upon: https://www.shadertoy.com/view/XsX3zB +#ifndef OPENGL +#define fract frac +#endif - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Snap_Percent< + string label = "Snap Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 7.5; +uniform float Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 2.5; +uniform float Resolution< + string label = "Resolution"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 16.0; +uniform bool Fractal = false; +uniform bool Use_Alpha_Layer = false; +uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; +uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform string Notes< + string widget_type = "info"; +> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; +float dot(float3 a, float3 b){ + return a.r*b.r+a.g*b.g+a.b*b.b; } +float dot4(float4 a, float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} +float snap(float x, float snap) +{ + return snap * round(x / max(0.01,snap)); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneCollection { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] -[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] -param( +float3 random3(float3 co) +{ + float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = fract(512.0 * j); + j *= .125; + result.x = fract(512.0 * j); + j *= .125; + result.y = fract(512.0 * j); + return result - 0.5; +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +/* 3d simplex noise */ +float simplex3d(float3 p) { + /* 1. find current tetrahedron T and it''s four vertices */ + /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ + /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ + + /* skew constants for 3d simplex functions */ + float F3 = 0.3333333; + float G3 = 0.1666667; + /* calculate s and x */ + float3 s = floor(p + dot(p, float3(F3,F3,F3))); + float3 x = p - s + dot(s, float3(G3,G3,G3)); -process { + /* calculate i1 and i2 */ + float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); + float3 i1 = e * (1.0 - e.zxy); + float3 i2 = 1.0 - e.zxy * (1.0 - e); + /* x1, x2, x3 */ + float3 x1 = x - i1 + G3; + float3 x2 = x - i2 + 2.0 * G3; + float3 x3 = x - 1.0 + 3.0 * G3; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + /* 2. find four surflets and store them in d */ + float4 w, d; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + /* calculate surflet weights */ + w.x = dot(x, x); + w.y = dot(x1, x1); + w.z = dot(x2, x2); + w.w = dot(x3, x3); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ + w = max(0.61 - w, 0.0); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + /* calculate surflet components */ + d.x = dot(random3(s), x); + d.y = dot(random3(s + i1), x1); + d.z = dot(random3(s + i2), x2); + d.w = dot(random3(s + 1.0), x3); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + /* multiply d by w^4 */ + w *= w; + w *= w; + d *= w; + /* 3. return the sum of the four surflets */ + return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); } -} +/* directional artifacts can be reduced by rotating each octave */ +float simplex3d_fractal(float3 m3) { + /* const matrices for 3d rotation */ +#ifdef OPENGL + float3x3 rot1 = float3x3( + float3(-0.37, 0.36, 0.85), + float3(-0.14, -0.93, 0.34), + float3(0.92, 0.01, 0.4 )); + float3x3 rot2 = float3x3( + float3(-0.55, -0.39, 0.74), + float3(0.33, -0.91, -0.24), + float3(0.77, 0.12, 0.63 )); + float3x3 rot3 = float3x3( + float3(-0.71, 0.52, -0.47), + float3(-0.08, -0.72, -0.68), + float3(-0.7, -0.45, 0.56 )); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransition { + float3 m = float3(m3.x, m3.y, m3.z); +#else + float3x3 rot1 = { + -0.37, 0.36, 0.85, + -0.14, -0.93, 0.34, + 0.92, 0.01, 0.4 }; + float3x3 rot2 = { + -0.55, -0.39, 0.74, + 0.33, -0.91, -0.24, + 0.77, 0.12, 0.63 }; + float3x3 rot3 = { + -0.71, 0.52, -0.47, + -0.08, -0.72, -0.68, + -0.7, -0.45, 0.56 }; + float3 m = {m3.x, m3.y, m3.z}; +#endif -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] -param( + return 0.5333333* simplex3d(mul(m, rot1)) + + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) + + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) + + 0.0666667 * simplex3d(8.0 * m); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET +{ + float time = snap(elapsed_time, Snap_Percent * .01); + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 p = v_in.uv.xy + float2( 0, -0.5); + float3 p3 = float3(p, time * (Speed_Percent * 0.01)); + + float pixel_alpha = 1.0; + // apply to mainImage rgba + if (Use_Alpha_Layer) { + p3 *= rgba.rgb; + pixel_alpha = rgba.a; + } + float value; -process { + if (Fractal) { + value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); + } + else { + value = simplex3d(p3 * Resolution); + } + //soften color + value = 0.5 + (0.5 * value); + float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + //use intensity to apply foreground and background colors + float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); + r = lerp(Back_Color, r, saturate(intensity)); + r.a = pixel_alpha; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + return lerp(rgba, r, Alpha_Percent * 0.01); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55452,218 +55029,222 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionDuration { - +function Get-OBSSmartDenoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] +[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +# Set the uSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uSigma')] +[Single] +$USigma, +# Set the uKSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uKSigma')] +[Single] +$UKSigma, +# Set the uThreshold of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uThreshold')] +[Single] +$UThreshold, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) -} +process { +$shaderName = 'smart_denoise' +$ShaderNoun = 'OBSSmartDenoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) +// Converted to OBS version of HLSL by Euiko on February 10, 2025 -} +#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI +#define INV_PI 0.31830988618379067153776752674503 - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionSettings { +uniform float uSigma< + string label = "Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 3; // max based on the webgl sample, which is 3 + float step = 0.01; +> = 5.0; // default value based on shadertoy +uniform float uKSigma< + string label = "K-Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 24; // max based on the webgl sample, which is 24 + float step = 0.01; +> = 7.0; // the default value is based on the webgl sample +uniform float uThreshold< + string label = "Edge Threshold"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 2; // max based on the webgl sample, which is 2 + float step = 0.01; +> = 0.190; // the default value is based on the webgl sample +// smartDeNoise - parameters +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// float2 uv - actual fragment coord +// float2 size - window size +// float sigma > 0 - sigma Standard Deviation +// float kSigma >= 0 - sigma coefficient +// kSigma * sigma --> radius of the circular kernel +// float threshold - edge sharpening threshold +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default +float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) +{ + float radius = round(kSigma * sigma); + float radQ = radius * radius; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] -param( + float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) + float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionSettings')] -[PSObject] -$TransitionSettings, + float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) + float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float4 centrPx = image.Sample(textureSampler, uv); + float zBuff = 0.0; + float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); -process { + float2 d; + for (d.x = -radius; d.x <= radius; d.x += 1.0) + { + float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend + d.y = -pt; + for (; d.y <= pt; d.y += 1.0) + { + float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; + float4 walkPx = image.Sample(textureSampler, uv + (d / size)); + float4 dC = walkPx - centrPx; + float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; + zBuff += deltaFactor; + aBuff += (walkPx * deltaFactor); + } + } + return aBuff / float4(zBuff, zBuff, zBuff, zBuff); +} +float4 mainImage(VertData v_in) : TARGET +{ + return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55672,233 +55253,270 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioBalance { - +function Get-OBSSpecularShineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] -[Alias('obs.powershell.websocket.SetInputAudioBalance')] +[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Hint of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('Hint')] +[String] +$Hint, +# Set the roughness of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('roughness')] +[Single] +$Roughness, +# Set the lightStrength of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('lightStrength')] +[Single] +$LightStrength, +# Set the LightPositionX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionX')] +[Single] +$LightPositionX, +# Set the LightPositionY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionY')] +[Single] +$LightPositionY, +# Set the flattenNormal of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('flattenNormal')] +[Single] +$FlattenNormal, +# Set the stretchNormalX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalX')] +[Single] +$StretchNormalX, +# Set the stretchNormalY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalY')] +[Single] +$StretchNormalY, +# Set the Light_Color of OBSSpecularShineShader +[Alias('Light_Color')] +[ComponentModel.DefaultBindingProperty('Light_Color')] +[Single[]] +$LightColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioBalance')] -[ValidateRange(0,1)] -[double] -$InputAudioBalance, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'specular-shine' +$ShaderNoun = 'OBSSpecularShineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft +uniform string Hint< + string widget_type = "info"; +> = "Try using a black color source with the additive blend mode!"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float roughness< + string label = "Roughness"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.25; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float lightStrength< + string label = "lightStrength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float LightPositionX< + string label = "Light Position X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float LightPositionY< + string label = "Light Position Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float flattenNormal< + string label = "Flatten Normal"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; -} +uniform float stretchNormalX< + string label = "Stretch Normal X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 4.0; + float step = 0.01; +> = 1; +uniform float stretchNormalY< + string label = "Stretch Normal Y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 4.0; + float step = 0.01; +> = 1; -} +uniform float3 Light_Color = {1,1,1}; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioMonitorType { +float Square(float a) { return a * a; } +float CookTorrance(float3 lightDir, float3 normal, float roughness) { + float3 h = normalize(lightDir + float3(0,0,1)); + float nh2 = Square(saturate(dot(normal, h))); + float lh2 = Square(saturate(dot(lightDir, h))); + float r2 = Square(roughness); + float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); + float normalization = roughness * 4.0 + 2.0; + return r2 / (d2 * max(0.1, lh2) * normalization); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] -param( +#define PI 3.14159265 -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float4 mainImage(VertData v_in) : TARGET +{ -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + float4 c0 = image.Sample(textureSampler, v_in.uv); + + float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); + + float2 normalUV = v_in.uv - 0.5; + normalUV.x /= stretchNormalX; + normalUV.y /= stretchNormalY; + normalUV += 0.5; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorType')] -[string] -$MonitorType, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); + normal.z *= -1; + normal = lerp(normal, float3(0,0,-1), flattenNormal); -process { + float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; + return float4(c0 + light,c0.a); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55907,116 +55525,219 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioSyncOffset { - +function Get-OBSSpotlightShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] +[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Speed_Percent of OBSSpotlightShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Single] +$SpeedPercent, +# Set the Focus_Percent of OBSSpotlightShader +[Alias('Focus_Percent')] +[ComponentModel.DefaultBindingProperty('Focus_Percent')] +[Single] +$FocusPercent, +# Set the Glitch of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Spotlight_Color of OBSSpotlightShader +[Alias('Spotlight_Color')] +[ComponentModel.DefaultBindingProperty('Spotlight_Color')] +[String] +$SpotlightColor, +# Set the Horizontal_Offset of OBSSpotlightShader +[Alias('Horizontal_Offset')] +[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] +[Single] +$HorizontalOffset, +# Set the Vertical_Offset of OBSSpotlightShader +[Alias('Vertical_Offset')] +[ComponentModel.DefaultBindingProperty('Vertical_Offset')] +[Single] +$VerticalOffset, +# Set the Notes of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] -[ValidateRange(-950,20000)] -[double] -$InputAudioSyncOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'spotlight' +$ShaderNoun = 'OBSSpotlightShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +uniform float Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform float Focus_Percent< + string label = "Focus Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 15.0; +uniform bool Glitch; +uniform float4 Spotlight_Color; +uniform float Horizontal_Offset< + string label = "Horizontal Offset"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Vertical_Offset< + string label = "Vertical Offset"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.5; +uniform string Notes< + string widget_type = "info"; +> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; +float4 mainImage(VertData v_in) : TARGET +{ + float speed = Speed_Percent * 0.01; + float focus = Focus_Percent; + if (Glitch) + { + speed *= ((rand_f * 2) - 1) * 0.01; + focus *= ((rand_f * 1.1) - 0.1); + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample( textureSampler, v_in.uv); + float3 lightsrc = float3(sin(elapsed_time * speed * PI * 0.667) *.5 + .5 + Horizontal_Offset, cos(elapsed_time * speed * PI) *.5 + .5 + Vertical_Offset, 1); + float3 light = normalize(lightsrc - float3( v_in.uv.x + (Horizontal_Offset * speed), v_in.uv.y + (Vertical_Offset * speed), 0)); + c0 *= pow(dot(light, float3(0, 0, 1)), focus) * Spotlight_Color; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + return c0; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56025,115 +55746,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioTracks { +function Get-OBSSwirlShader { + +[Alias('Set-OBSSwirlShader','Add-OBSSwirlShader')] +param( +# Set the radius of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the angle of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the center_x of OBSSwirlShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSSwirlShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the animate of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the inverse of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('inverse')] +[Management.Automation.SwitchParameter] +$Inverse, +# Set the notes of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + +process { +$shaderName = 'Swirl' +$ShaderNoun = 'OBSSwirlShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; // +uniform float angle< + string label = "Angle"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.0; + float step = 0.01; +> = 270.0; // -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] -[Alias('obs.powershell.websocket.SetInputAudioTracks')] -param( +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +uniform bool animate = false; +uniform bool inverse = false; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, +uniform string notes< + string widget_type = "info"; +> = "Distorts the screen, twisting the image in a circular motion." -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioTracks')] -[PSObject] -$InputAudioTracks, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET +{ + + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + v_out.uv = 1. * v_in.uv - center; + + center.x /= ar; + v_out.uv.x /= ar; + + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float percent = (radius-dist)/(radius); + percent = inverse == false ? percent : 1 - percent; -process { + float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv ); + } + +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56142,232 +55986,400 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputMute { - +function Get-OBSTetraShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] -[Alias('obs.powershell.websocket.SetInputMute')] +[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the redR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redR')] +[Single] +$RedR, +# Set the redG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redG')] +[Single] +$RedG, +# Set the redB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redB')] +[Single] +$RedB, +# Set the yelR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelR')] +[Single] +$YelR, +# Set the yelG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelG')] +[Single] +$YelG, +# Set the yelB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelB')] +[Single] +$YelB, +# Set the grnR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnR')] +[Single] +$GrnR, +# Set the grnG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnG')] +[Single] +$GrnG, +# Set the grnB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnB')] +[Single] +$GrnB, +# Set the cynR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynR')] +[Single] +$CynR, +# Set the cynG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynG')] +[Single] +$CynG, +# Set the cynB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynB')] +[Single] +$CynB, +# Set the bluR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluR')] +[Single] +$BluR, +# Set the bluG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluG')] +[Single] +$BluG, +# Set the bluB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluB')] +[Single] +$BluB, +# Set the magR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magR')] +[Single] +$MagR, +# Set the magG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magG')] +[Single] +$MagG, +# Set the magB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magB')] +[Single] +$MagB, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputMuted')] -[switch] -$InputMuted, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'tetra' +$ShaderNoun = 'OBSTetraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Tetrahedral Interpolation Shader for OBS +uniform float redR< + string label = "Red in Red"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float redG< + string label = "Green in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float redB< + string label = "Blue in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float yelR< + string label = "Red in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float yelG< + string label = "Green in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + +uniform float yelB< + string label = "Blue in Yellow"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float grnR< + string label = "Red in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float grnG< + string label = "Green in Green"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + +uniform float grnB< + string label = "Blue in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float cynR< + string label = "Red in Cyan"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float cynG< + string label = "Green in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + +uniform float cynB< + string label = "Blue in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float bluR< + string label = "Red in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -} +uniform float bluG< + string label = "Green in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float bluB< + string label = "Blue in Blue"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -} +uniform float magR< + string label = "Red in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputName { +uniform float magG< + string label = "Green in Magenta"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float magB< + string label = "Blue in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] -[Alias('obs.powershell.websocket.SetInputName')] -param( -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { + float r = RGBimage.x; + float g = RGBimage.y; + float b = RGBimage.z; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + float3 wht = float3(1.0, 1.0, 1.0); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newInputName')] -[string] -$NewInputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if (r > g) { + if (g > b) { + // r > g > b + return r * red + g * (yel - red) + b * (wht - yel); + } else if (r > b) { + // r > b > g + return r * red + g * (wht - mag) + b * (mag - red); + } else { + // b > r > g + return r * (mag - blu) + g * (wht - mag) + b * blu; + } + } else { + if (b > g) { + // b > g > r + return r * (wht - cyn) + g * (cyn - blu) + b * blu; + } else if (b > r) { + // g > b > r + return r * (wht - cyn) + g * grn + b * (cyn - grn); + } else { + // g > r > b + return r * (yel - grn) + g * grn + b * (wht - yel); + } + } +} +float4 mainImage(VertData v_in) : TARGET +{ + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float alpha = inputColor.a; -process { + float3 red = float3(redR, redG, redB); + float3 yel = float3(yelR, yelG, yelB); + float3 grn = float3(grnR, grnG, grnB); + float3 cyn = float3(cynR, cynG, cynB); + float3 blu = float3(bluR, bluG, bluB); + float3 mag = float3(magR, magG, magB); + float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); + return float4(outputColor, alpha); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56376,244 +56388,397 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputSettings { - +function Get-OBSThermalShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] -[Alias('obs.powershell.websocket.SetInputSettings')] +[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, - +# Set the strength of OBSThermalShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'thermal' +$ShaderNoun = 'OBSThermalShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/mdKXzG +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.1; +> = 100.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float greyScale(float3 c) { + return 0.29 * c.r + 0.60 * c.g + 0.11; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float3 heatMap(float greyValue) { + float3 heat; + heat.r = smoothstep(0.5, 0.8, greyValue); + if(greyValue >= 0.8333) { + heat.r *= (1.1 - greyValue) * 5.0; + } + if(greyValue > 0.6) { + heat.g = smoothstep(1.0, 0.7, greyValue); + } else { + heat.g = smoothstep(0.0, 0.7, greyValue); + } + heat.b = smoothstep(1.0, 0.0, greyValue); + if(greyValue <= 0.3333) { + heat.b *= greyValue / 0.3; + } + return heat; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float greyValue = greyScale(c.rgb); + float3 h = heatMap(greyValue*(strength/100.0)); + return float4(h.r, h.g, h.b, c.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } -} + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputVolume { + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] -[Alias('obs.powershell.websocket.SetInputVolume')] -param( + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeMul')] -[ValidateRange(0,20)] -[double] -$InputVolumeMul, +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSTvCrtSubpixelShader { +[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] +param( +# Set the channelWidth of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelWidth')] +[Int32] +$ChannelWidth, +# Set the channelHeight of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelHeight')] +[Int32] +$ChannelHeight, +# Set the hGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('hGap')] +[Int32] +$HGap, +# Set the vGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('vGap')] +[Int32] +$VGap, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeDb')] -[ValidateRange(-100,26)] -[double] -$InputVolumeDb, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'tv-crt-subpixel' +$ShaderNoun = 'OBSTvCrtSubpixelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro +// width of a single color channel in pixels +uniform int channelWidth< + string label = "Channel Width"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// height of color channels in pixels +uniform int channelHeight< + string label = "Channel Height"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 3; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// horizontal distance between two neighboring pixels +uniform int hGap< + string label = "Horizontal Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +// vertical distance between two neighboring pixels +uniform int vGap< + string label = "Vertical Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; + +float4 mainImage(VertData v_in) : TARGET +{ + float columns = float(channelWidth * 3 + hGap); + float pixelHeight = float(channelHeight + vGap); + + float2 fragCoord = v_in.uv * uv_size; + float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); + float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); + float2 sampleUv = pixel / sampleRes; + + // color of sample point + float4 col = image.Sample(textureSampler, sampleUv); + + int column = int(fragCoord.x) % (channelWidth * 3 + hGap); + + // set color based on which channel this fragment corresponds to + if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); + else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); + else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); + else col = float4(0.0, 0.0, 0.0, col.a); + + // offset every other column of pixels + int height = int(pixelHeight); + if (int(pixel.x) % 2 == 0) { + if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); + } else { + if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); + } + + // Output to screen + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56622,228 +56787,202 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaInputCursor { - +function Get-OBSTwistShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] -[Alias('obs.powershell.websocket.SetMediaInputCursor')] +[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the center_x_percent of OBSTwistShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSTwistShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the power of OBSTwistShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the rotation of OBSTwistShader +[ComponentModel.DefaultBindingProperty('rotation')] +[Single] +$Rotation, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursor')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$MediaCursor, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'twist' +$ShaderNoun = 'OBSTwistShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int center_x_percent< + string label = "center x percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.3; +uniform float rotation< + string label = "rotation"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.001; +> = 2.0; +#ifndef OPENGL +#define mat2 float2x2 +#endif - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +mat2 rotate(float angle){ + return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float d = distance(center_pos,v_in.uv); + if(d > power){ + return image.Sample(textureSampler, v_in.uv); + } + float r = (cos(d*3.14159265359/power) +1)/2 * rotation; + float2 pos = v_in.uv - center_pos; + pos = mul(pos, rotate(r)); + pos += center_pos; + return image.Sample(textureSampler, pos); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSOutputSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] -[Alias('obs.powershell.websocket.SetOutputSettings')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputSettings')] -[PSObject] -$OutputSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56852,232 +56991,314 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSPersistentData { - +function Get-OBSTwoPassDropShadowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] -[Alias('obs.powershell.websocket.SetPersistentData')] +[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotValue')] -[PSObject] -$SlotValue, -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSTwoPassDropShadowShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSTwoPassDropShadowShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSTwoPassDropShadowShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSTwoPassDropShadowShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSTwoPassDropShadowShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSTwoPassDropShadowShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the shadow_offset_x of OBSTwoPassDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSTwoPassDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSTwoPassDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the shadow_color of OBSTwoPassDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] +[String] +$ShadowColor, +# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader +[Alias('is_alpha_premultiplied')] +[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] +[Management.Automation.SwitchParameter] +$IsAlphaPremultiplied, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'two-pass-drop-shadow' +$ShaderNoun = 'OBSTwoPassDropShadowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; } +uniform int shadow_offset_x< + string label = "shadow offset x"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_offset_y< + string label = "shadow offset y"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +>; -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSProfileParameter { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] -[Alias('obs.powershell.websocket.SetProfileParameter')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, +uniform float4 shadow_color; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterValue')] -[string] -$ParameterValue, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform bool is_alpha_premultiplied; +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float sampled_shadow_alpha = 0; + + for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; + } + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); +} -process { +float4 mainImage_2_end(VertData v_in) : TARGET +{ + int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, + v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); + + float sampled_shadow_alpha = 0; + + for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) + { + float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; + } + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); +} +technique Draw +{ + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } + + pass p1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage_2_end(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57086,228 +57307,260 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSRecordDirectory { - +function Get-OBSVCRShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] -[Alias('obs.powershell.websocket.SetRecordDirectory')] +[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('recordDirectory')] -[string] -$RecordDirectory, -# If set, will return the information that would otherwise be sent to OBS. +# Set the vertical_shift of OBSVCRShader +[Alias('vertical_shift')] +[ComponentModel.DefaultBindingProperty('vertical_shift')] +[Single] +$VerticalShift, +# Set the distort of OBSVCRShader +[ComponentModel.DefaultBindingProperty('distort')] +[Single] +$Distort, +# Set the vignet of OBSVCRShader +[ComponentModel.DefaultBindingProperty('vignet')] +[Single] +$Vignet, +# Set the stripe of OBSVCRShader +[ComponentModel.DefaultBindingProperty('stripe')] +[Single] +$Stripe, +# Set the vertical_factor of OBSVCRShader +[Alias('vertical_factor')] +[ComponentModel.DefaultBindingProperty('vertical_factor')] +[Single] +$VerticalFactor, +# Set the vertical_height of OBSVCRShader +[Alias('vertical_height')] +[ComponentModel.DefaultBindingProperty('vertical_height')] +[Single] +$VerticalHeight, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'VCR' +$ShaderNoun = 'OBSVCRShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/ldjGzV +//Converted to OpenGL by Exeldro February 19, 2022 +uniform float vertical_shift< + string label = "vertical shift"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.4; +uniform float distort< + string label = "distort"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 5.0; + float step = 0.001; +> = 1.2; +uniform float vignet< + string label = "vignet"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float stripe< + string label = "stripe"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_factor< + string label = "vertical factor"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_height< + string label = "vertical height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 30.0; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float onOff(float a, float b, float c) +{ + return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); } +float ramp(float y, float start, float end) +{ + float inside = step(start,y) - step(end,y); + float fact = (y-start)/(end-start)*inside; + return (1.-fact) * inside; + +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemBlendMode { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] -[string] -$SceneItemBlendMode, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float stripes(float2 uv) +{ + return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; +} +float4 getVideo(float2 uv) +{ + float2 look = uv; + float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); + look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; + float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + + (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); + look.y = modu((look.y + vShift) , 1.); + return image.Sample(textureSampler, look); +} -process { +float2 screenDistort(float2 uv) +{ + uv -= float2(.5,.5); + uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); + uv += float2(.5,.5); + return uv; +} +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv = screenDistort(uv); + float4 video = getVideo(uv); + float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); + float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; + video += stripes(uv); + video *= vignette; + video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; + return float4(video.r, video.g, video.b ,1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57316,121 +57569,306 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemEnabled { - +function Get-OBSVHSShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] -[Alias('obs.powershell.websocket.SetSceneItemEnabled')] +[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the range of OBSVHSShader +[ComponentModel.DefaultBindingProperty('range')] +[Single] +$Range, +# Set the offsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('offsetIntensity')] +[Single] +$OffsetIntensity, +# Set the noiseQuality of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseQuality')] +[Single] +$NoiseQuality, +# Set the noiseIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseIntensity')] +[Single] +$NoiseIntensity, +# Set the colorOffsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] +[Single] +$ColorOffsetIntensity, +# Set the Alpha_Percentage of OBSVHSShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Image of OBSVHSShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSVHSShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Color_To_Replace of OBSVHSShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Apply_To_Specific_Color of OBSVHSShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'VHS' +$ShaderNoun = 'OBSVHSShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 +//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 +//Converted to OpenGL by Exeldro February 19, 2022 +//Use improved input fields by Exeldro April 15, 2023 +uniform float range< + string label = "Wave size (0.05)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.20; + float step = 0.01; +> = 0.05; +uniform float offsetIntensity< + string label = "Offset intensity (0.02)"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 0.20; + float step = 0.01; +> = 0.02; +uniform float noiseQuality< + string label = "Noise number of lines (250)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 1000.0; + float step = 10.0; +> = 250.0; +uniform float noiseIntensity< + string label = "Noise intensity (0.88)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.88; +uniform float colorOffsetIntensity< + string label = "Color offset intensity (1.3)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.1; +> = 1.3; +uniform float Alpha_Percentage< + string label = "Aplha percentage (100.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform float4 Color_To_Replace; +uniform bool Apply_To_Specific_Color; +float dot2(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float rand(float2 co) +{ + return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float verticalBar(float pos, float uvY, float offset) +{ + float edge0 = (pos - range); + float edge1 = (pos + range); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + float x = smoothstep(edge0, pos, uvY) * offset; + x -= smoothstep(pos, edge1, uvY) * offset; + return x; +} + +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} + +float dot4(float4 a,float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + for (float i = 0.0; i < 0.71; i += 0.1313) + { + float d = modu(elapsed_time * i, 1.7); + float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); + o *= offsetIntensity; + uv.x += verticalBar(d, uv.y, o); + } + float uvY = uv.y; + uvY *= noiseQuality; + uvY = float(int(uvY)) * (1.0 / noiseQuality); + float noise = rand(float2(elapsed_time * 0.00001, uvY)); + uv.x += noise * noiseIntensity / 100.0; + + float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; + float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; + + float4 rgba = image.Sample(textureSampler, uv); + float r = image.Sample(textureSampler, uv + offsetR).r; + float g = image.Sample(textureSampler, uv + offsetG).g; + float b = rgba.b; + + rgba = float4(r, g, b, rgba.a); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); + if (Replace_Image_Color) + color = float4(luma,luma,luma,luma); + rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } + + return rgba; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57439,122 +57877,201 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemIndex { - +function Get-OBSVignettingShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] -[Alias('obs.powershell.websocket.SetSceneItemIndex')] +[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the innerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('innerRadius')] +[Single] +$InnerRadius, +# Set the outerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('outerRadius')] +[Single] +$OuterRadius, +# Set the opacity of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# Set the notes of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemIndex, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'vignetting' +$ShaderNoun = 'OBSVignettingShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +uniform float innerRadius< + string label = "inner radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.9; +uniform float outerRadius< + string label = "outer radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.5; +uniform float opacity< + string label = "opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.8; +uniform string notes< + string widget_type = "info"; +> = "inner radius will always be shown, outer radius is the falloff"; +float4 mainImage(VertData v_in) : TARGET +{ + float PI = 3.1415926535897932384626433832795;//acos(-1); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 c0 = image.Sample(textureSampler, v_in.uv); + float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; + + float xTrans = (v_in.uv.x * 2) - 1; + float yTrans = 1 - (v_in.uv.y * 2); + + float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); + + float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); + float factor = 1 - subtraction; + + float4 vignetColor = c0 * factor; + vignetColor *= verticalDim; + + vignetColor *= opacity; + c0 *= 1-opacity; + + float4 output_color = c0 + vignetColor; + + return float4(output_color); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57563,244 +58080,230 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemLocked { - +function Get-OBSVoronoiPixelationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] -[Alias('obs.powershell.websocket.SetSceneItemLocked')] +[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the pixH of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('pixH')] +[Single] +$PixH, +# Set the alternative of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('alternative')] +[Management.Automation.SwitchParameter] +$Alternative, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemLocked')] -[switch] -$SceneItemLocked, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'voronoi-pixelation' +$ShaderNoun = 'OBSVoronoiPixelationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro +uniform float pixH< + string label = "Size"; + string widget_type = "slider"; + float minimum = 4.0; + float maximum = 500.0; + float step = 0.01; +> = 100.0; +uniform bool alternative; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float2 random2( float2 p ) { + return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); +} +float2 randomSpin(float2 p, float f){ + return 1.0 * float2( + cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), + sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); +} +float4 VoronoiPixelation(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); + + float2 point1 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point1 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + } + float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x ; - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - + return image.Sample(textureSampler, nPoint); } +float4 VoronoiPixelation2(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemTransform { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] -[Alias('obs.powershell.websocket.SetSceneItemTransform')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemTransform')] -[PSObject] -$SceneItemTransform, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); -process { + float2 point2 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point2 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; + } + } + } + float2 nPoint = pixExt + relClos + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x; + + return image.Sample(textureSampler, nPoint); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + if (alternative) { + return VoronoiPixelation2(v_in.uv, pixH); + } else { + return VoronoiPixelation(v_in.uv, pixH); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57809,238 +58312,379 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneName { +function Get-OBSWalkingDeadPixelFixerShader { - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] -[Alias('obs.powershell.websocket.SetSceneName')] +[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Scan_Width of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Width')] +[ComponentModel.DefaultBindingProperty('Scan_Width')] +[Int32] +$ScanWidth, +# Set the Scan_Height of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Height')] +[ComponentModel.DefaultBindingProperty('Scan_Height')] +[Int32] +$ScanHeight, +# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_X')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] +[Int32] +$ScanOffsetX, +# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] +[Int32] +$ScanOffsetY, +# Set the Show_Border of OBSWalkingDeadPixelFixerShader +[Alias('Show_Border')] +[ComponentModel.DefaultBindingProperty('Show_Border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader +[Alias('Contrast_Threshold')] +[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] +[Single] +$ContrastThreshold, +# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Min_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] +[Int32] +$MinClusterSize, +# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Max_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] +[Int32] +$MaxClusterSize, +# Set the Show_Green of OBSWalkingDeadPixelFixerShader +[Alias('Show_Green')] +[ComponentModel.DefaultBindingProperty('Show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the Bypass of OBSWalkingDeadPixelFixerShader +[ComponentModel.DefaultBindingProperty('Bypass')] +[Management.Automation.SwitchParameter] +$Bypass, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newSceneName')] -[string] -$NewSceneName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'walking-dead-pixel-fixer' +$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter +// by Eegee http://github.com/eegee/ +// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixels located in the user configured scan area and passing the threshold settings will have its colors +// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. +// +// Changelog: +// 0.01 - Initial release +// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. +uniform int Scan_Width< + string label = "Scan area width"; + int minimum = 1; + int maximum = 2560; + int step = 1; +> = 75; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int Scan_Height< + string label = "Scan area height"; + int minimum = 1; + int maximum = 1440; + int step = 1; +> = 120; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int Scan_Offset_X< + string label = "Scan area offset X"; + int minimum = 0; + int maximum = 2560; + int step = 1; +> = 110; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform int Scan_Offset_Y< + string label = "Scan area offset Y"; + int minimum = 0; + int maximum = 1440; + int step = 1; +> = 20; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform bool Show_Border< + string label = "Show scan area border in red"; + string widget_type = "checkbox"; +> = true; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Contrast_Threshold< + string label = "Contrast threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; -} +uniform int Min_Cluster_Size< + string label = "Min cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 324; +uniform int Max_Cluster_Size< + string label = "Max cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 400; -} +uniform bool Show_Green< + string label = "Show matches in green"; + string widget_type = "checkbox"; +> = true; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneSceneTransitionOverride { +uniform bool Bypass< + string label = "Bypass"; + string widget_type = "checkbox"; +> = false; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] -param( +#define SAMPLE_RADIUS 9 -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +float luminance(float3 color) +{ + return dot(color, float3(0.299, 0.587, 0.114)); +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) +{ + float3 sumColor = float3(0.0, 0.0, 0.0); + float weightSum = 0.0; + contrastCount = 0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, + float3 centerColor = image.Sample(textureSampler, uv).rgb; + float centerLum = luminance(centerColor); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) + { + for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) + { + if (x == 0 && y == 0) continue; + float2 offset = float2(x, y) / uv_size; + float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); -process { + // skip central pixel + if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && + ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) + continue; + float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; + float lum = luminance(sampleColor); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float weight = 1.0; + sumColor += sampleColor * weight; + weightSum += weight; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (abs(lum - centerLum) >= contrastThreshold) + contrastCount++; } + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if (weightSum > 0) + { + avgColor = sumColor / weightSum; + } + else + { + avgColor = centerColor; + } + avgLuminance = luminance(avgColor); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; + + float4 tex = image.Sample(textureSampler, uv); + float3 color = tex.rgb; + + if (!Bypass) + { + int pixX = (int)round(pos.x); + int pixY = (int)round(pos.y); + + int borderwidth = 2; + + bool insideScan = + (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && + (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); + + bool borderingScan = + (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && + (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); + + if (insideScan) + { + float3 avgColor; + float avgLum; + int contrastCount; + + sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); + + if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) + { + color = avgColor; + + if (Show_Green) + { + int left = pixX - borderwidth; + int right = pixX + borderwidth; + int top = pixY - borderwidth; + int bottom = pixY + borderwidth; + + bool onOutline = + abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || + abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; + + if (onOutline) + return float4(0.0, 1.0, 0.0, 1.0); } } } + else if (Show_Border && borderingScan) + { + return float4(1.0, 0.0, 0.0, 0.5); + } + } + return float4(color, tex.a); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58049,120 +58693,283 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterEnabled { - +function Get-OBSZigZagShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] -[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] +[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] param( - +# Set the radius of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the angle of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the period of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('period')] +[Single] +$Period, +# Set the amplitude of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the center_x of OBSZigZagShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSZigZagShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the phase of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('phase')] +[Single] +$Phase, +# Set the animate of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('animate')] +[Int32] +$Animate, +# Set the notes of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'ZigZag' +$ShaderNoun = 'OBSZigZagShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.0; +uniform float angle< + string label = "angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 180.0; +uniform float period< + string label = "period"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; +uniform float amplitude< + string label = "amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; + +uniform float center_x< + string label = "center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; +uniform float center_y< + string label = "center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; + +uniform float phase< + string label = "phase"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform int animate< + string label = "animate"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "No"; + int option_1_value = 1; + string option_1_label = "Amplitude"; + int option_2_value = 2; + string option_2_label = "Time"; +> = 0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, +uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterEnabled')] -[switch] -$FilterEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET +{ + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; -process { + v_out.uv = 1. * v_in.uv - center; + + center.x /= ar; + v_out.uv.x /= ar; + + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float percent = (radius-dist)/radius; + float theta = percent * percent * + ( + animate == 1 ? + amplitude * sin(elapsed_time) : + amplitude + ) + * sin(percent * percent / period * radians(angle) + (phase + + ( + animate == 2 ? + elapsed_time : + 0 + ))); + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } + +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58171,243 +58978,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterIndex { - +function Get-OBSZoomBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] -[Alias('obs.powershell.websocket.SetSourceFilterIndex')] +[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] param( - +# Set the samples of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the magnitude of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('magnitude')] +[Single] +$Magnitude, +# Set the speed_percent of OBSZoomBlurShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the ease of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the glitch of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$FilterIndex, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'zoom_blur' +$ShaderNoun = 'OBSZoomBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl +//for Media Player Classic HC or BE +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int samples < + string label = "Samples"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 32; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform int speed_percent < + string label = "Speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform bool glitch; +uniform string notes< + string widget_type = "info"; +> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; } +float4 mainImage(VertData v_in) : TARGET +{ + float speed = speed_percent * 0.01; -} + // circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterName { + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); + b = Styler(t, 0, c, d, ease); + float sample_speed = max(samples * b, 1.0); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] -[Alias('obs.powershell.websocket.SetSourceFilterName')] -param( + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, + float xTrans = (v_in.uv.x*2)-1; + float yTrans = 1-(v_in.uv.y*2); + + float angle = atan(yTrans/xTrans) + PI; + if (sign(xTrans) == 1) { + angle+= PI; + } + float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, + float2 currentCoord; + float4 accumulatedColor = float4(0,0,0,0); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor = currentColor; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newFilterName')] -[string] -$NewFilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + accumulatedColor = c0/sample_speed; + for(int i = 1; i< sample_speed; i++) { + float currentRadius ; + // Distance to center dependent + currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); + // Continuous; + // currentRadius = max(0,radius - (0.0004 * i)); -process { + currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; + currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); + + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor += currentColor/sample_speed; + + } + return accumulatedColor; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58416,125 +59236,177 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterSettings { - +function Get-OBSZoomShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] -[Alias('obs.powershell.websocket.SetSourceFilterSettings')] +[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] param( - +# Set the center_x_percent of OBSZoomShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the power of OBSZoomShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'zoom' +$ShaderNoun = 'OBSZoomShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58543,110 +59415,197 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStreamServiceSettings { - +function Get-OBSZoomXYShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] -[Alias('obs.powershell.websocket.SetStreamServiceSettings')] +[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceType')] -[string] -$StreamServiceType, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceSettings')] -[PSObject] -$StreamServiceSettings, -# If set, will return the information that would otherwise be sent to OBS. +# Set the center_x_percent of OBSZoomXYShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomXYShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the x_power of OBSZoomXYShader +[Alias('x_power')] +[ComponentModel.DefaultBindingProperty('x_power')] +[Single] +$XPower, +# Set the y_power of OBSZoomXYShader +[Alias('y_power')] +[ComponentModel.DefaultBindingProperty('y_power')] +[Single] +$YPower, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Zoom_XY' +$ShaderNoun = 'OBSZoomXYShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Zoom XY Shader +// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// The allow for an independent Horizontal and Vertical Zoom. + +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float x_power< + string label = "x power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +uniform float y_power< + string label = "y power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58655,2510 +59614,3415 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStudioModeEnabled { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] -[Alias('obs.powershell.websocket.SetStudioModeEnabled')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('studioModeEnabled')] -[switch] -$StudioModeEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +function Set-OBSAudioOutputSource { + + + #> + [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] + param( + # The name of the audio device. + # This name or device ID of the audio device that should be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ItemValue','ItemName','DeviceID')] + [string] + $AudioDevice, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, -process { + # The name of the input. + # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + begin { + # Audio Output sources have an inputKind of 'wasapi_output_capture'. + $inputKind = "wasapi_output_capture" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + + if (-not $myParameters["AudioDevice"]) { + $myParameters["AudioDevice"] = "default" } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSTBarPosition { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] -[Alias('obs.powershell.websocket.SetTBarPosition')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('position')] -[ValidateRange(0,1)] -[double] -$Position, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('release')] -[switch] -$Release, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + + if ($myParameters["AudioDevice"]) { + $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] + } + else { + $Name = $myParameters["Name"] = "AudioOutput" + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] } } + } + + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = $inputKind + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } + + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id + foreach ($deviceInfo in $possibleDevices) { + if ( + ($deviceInfo.itemName -eq $AudioDevice) -or + ($deviceInfo.ItemValue -eq $AudioDevice) -or + ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or + ($deviceInfo.ItemValue -like "*$AudioDevice*") -or + ($deviceInfo.ItemName -like "*$AudioDevice*") + ) { + $myParameterData["device_id"] = $deviceInfo.itemValue + break + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return } + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Scene"] + } + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + } + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSBrowserSource { + + + [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] + param( + # The uri or file path to display. + # If the uri points to a local file, this will be preferred + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url', 'Href','Path','FilePath','FullName')] + [uri] + $Uri, + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSVideoSettings { + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] -[Alias('obs.powershell.websocket.SetVideoSettings')] -param( + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsNumerator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsNumerator, + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsDenominator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsDenominator, + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseWidth')] -[ValidateRange(1,4096)] -[double] -$BaseWidth, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseHeight')] -[ValidateRange(1,4096)] -[double] -$BaseHeight, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputWidth')] -[ValidateRange(1,4096)] -[double] -$OutputWidth, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputHeight')] -[ValidateRange(1,4096)] -[double] -$OutputHeight, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -process { + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + $inputKind = "browser_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } + if ($uri.Scheme -eq 'File') { + if (Test-Path $uri.AbsolutePath) { + $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + else + { + if (Test-Path $uri) { + $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) + $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true + } else { + $myParameterData["url"] = "$uri" + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($uri.Segments) { + $uri.Segments[-1] + } elseif ($uri -match '[\\/]') { + @($uri -split '[\\/]')[-1] + } else { + $uri + } + } -} + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 -} + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Start-OBSOutput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] -[Alias('obs.powershell.websocket.StartOutput')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +function Set-OBSColorSource { + + + [Alias('Add-OBSColorSource','Get-OBSColorSource')] + param( + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, -process { + [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + begin { + $inputKind = "color_source_v3" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] -[Alias('obs.powershell.websocket.StartRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $hexChar = [Regex]::new('[0-9a-f]') + $hexColors = @($hexChar.Matches($Color)) - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + switch ($hexColors.Length) { + 8 { + #full rgba + $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') + } + 6 { + #rgb only, assume ff for alpha + $alpha = 0xff + $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + } + 4 { + #short rgba + $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') + } + 3 { + #short rgb, assume f for alpha + $alpha = 0xff + $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + } + 0 { + # No color provided, default to transparent black + $alpha = 0 + $red = 0 + $green = 0 + $blue = 0 + } } + + $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + $realColor = [uint32]::Parse($hexColor,'HexNumber') + + + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "#$hexColor" } + + $myParameterData = [Ordered]@{color=$realColor} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "color_source_v3" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } -} + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } -} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Start-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] -[Alias('obs.powershell.websocket.StartReplayBuffer')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - +function Set-OBSDisplaySource { + + + [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] + param( + # The monitor number. + # This the number of the monitor you would like to capture. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("monitor")] + [Alias('MonitorNumber','Display','DisplayNumber')] + [int] + $Monitor = 1, -process { + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("capture_cursor")] + [switch] + $CaptureCursor, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSStream { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] -[Alias('obs.powershell.websocket.StartStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + } + } + } -process { + # Users like 1 indexed, computers like zero-indexed. + $myParameterData["monitor"] = $Monitor - 1 + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "Display $($Monitor)" + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "monitor_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSMarkdownSource { + + + [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] + param( + # The markdown text, or the path to a markdown file + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Markdown, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + # The css style used to render the markdown. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", -} + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSVirtualCam { + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] -[Alias('obs.powershell.websocket.StartVirtualCam')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -process { + } + begin { + $inputKind = "markdown_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + $IsGet = $MyInvocation.InvocationName -like "Get-*" + $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if ( + $IsGet -or + ($NoVerb -and -not $NonNameParameters) + ) { + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $markdownAsUri = $null + if ($Markdown -like '*.md') { + $markdownAsUri = $markdown -as [uri] + if ($markdownAsUri.Scheme -eq 'File') { + $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["markdown_source"] = 1 + } + else { + } + } else { + $myParameterData["text"] = $Markdown + $myParameterData["markdown_source"] = 0 } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($markdownAsUri.Segments) { + $markdownAsUri.Segments[-1] + } elseif ($markdownAsUri -match '[\\/]') { + @($markdownAsUri -split '[\\/]')[-1] + } elseif ($markdownAsUri) { + $markdownAsUri + } else { + "Markdown" + } + } + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = "markdown_source" + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSMediaSource { + + + [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file')] + [string] + $FilePath, + # If set, the source will close when it is inactive. + # By default, this will be set to true. + # To explicitly set it to false, use -CloseWhenInactive:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("close_when_inactive")] + [switch] + $CloseWhenInactive, -} + # If set, the source will automatically restart. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("looping")] + [Alias('Looping')] + [switch] + $Loop, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSOutput { + # If set, will use hardware decoding, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("hw_decode")] + [Alias('HardwareDecoding','hw_decode')] + [switch] + $UseHardwareDecoding, + # If set, will clear the output on the end of the media. + # If this is set to false, the media will freeze on the last frame. + # This is set to true by default. + # To explicitly set to false, use -ClearMediaEnd:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("clear_on_media_end")] + [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] + [switch] + $ClearOnMediaEnd, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] -[Alias('obs.powershell.websocket.StopOutput')] -param( + # Any FFMpeg demuxer options. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ffmpeg_options")] + [Alias('FFMpegOptions', 'FFMpeg_Options')] + [string] + $FFMpegOption, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -process { + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "ffmpeg_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ((Test-Path $FilePath)) { + $FilePathItem = Get-Item -Path $FilePath + $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' } -} + + if ($myParameters['InputSettings']) { + $keys = + @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { + $myParameters['InputSettings'].Keys + } else { + foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { + $prop.Name + } + }) -} + foreach ($key in $keys) { + $myParameterData[$key] = $myParameters['InputSettings'].$key + } + $myParameterData.remove('inputSettings') + } -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] -[Alias('obs.powershell.websocket.StopRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - + if (-not $Name) { + + $Name = $myParameters["Name"] = + if ($FilePathItem.Name) { + $FilePathItem.Name + } else { + "Media" + } + + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # Otherwise, if we had a result + if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } - + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSoundCloudSource { + + + [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] + param( + # The uri to display. This must point to a SoundCloud URL. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url','SoundCloudUri','SoundCloudUrl')] + [uri] + $Uri, + # If set, will not autoplay. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoAutoPlay, -} + # If set, will not display album artwork. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoArtwork, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSReplayBuffer { + # If set, will not display play count. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoPlayCount, + # If set, will not display uploader info. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoUploaderInfo, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] -[Alias('obs.powershell.websocket.StopReplayBuffer')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If provided, will start playing at a given track number. + [Parameter(ValueFromPipelineByPropertyName)] + [int] + $TrackNumber, + # If set, will show a share link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowShare, -process { + # If set, will show a download link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowDownload, + # If set, will show a buy link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowBuy, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The color used for the SoundCloud audio bars and buttons. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, -} + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, -} + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSStream { + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + # The name of the input. + # If no name is provided, then "SoundCloud" will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] -[Alias('obs.powershell.websocket.StopStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -process { + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + # Sound Cloud Sources are really Browser Sources. + $inputKind = "browser_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { + Write-Error "URI must be from SoundCloud.com" + return } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if ($uri.Query) { + $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } | + Where-Object { + $_.Settings['LocalFile'] -like '*.SoundCloud.*' } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # (either way, if we were called Get- or with no verb, we're done now). + return } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight } -} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } -} + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSVirtualCam { + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } + $MyObsPowerShellPath = if ($home) { + Join-Path $home ".obs-powershell" + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] -[Alias('obs.powershell.websocket.StopVirtualCam')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $ThisSoundCloudSourceFileName = + if ($name) { + "${name}.SoundCloudSource.html" + } else { + "SoundCloudSource.html" + } + $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName + -process { + $soundCloudSrc = @( + "https://w.soundcloud.com/player/?url=" + $Uri + "&" + @( + if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} + if ($color) { "color=$color" -replace "\#",'%23'} + if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} + if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } + if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} + if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } + if ($ShowDownload) { "download=true"} else { "download=false" } + if ($ShowBuy) { "buying=true"} else { "buying=false" } + if ($ShowShare) { "sharing=true"} else { "sharing=false" } + ) -join '&' + ) -join '' + + $soundCloudWidget = @( + "" + "" + "" + "" + "" + ) -join ' ' + $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force + + $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if (-not $Name) { + $Name = $myParameters['Name'] = 'SoundCloud' + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } } - - -} - #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSInputMute { +function Set-OBSSwitchSource { + + + [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Sources')] + [string[]] + $SourceList, + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." + } + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] -[Alias('obs.powershell.websocket.ToggleInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + # If set, the list of sources will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + # If set, will switch between sources. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch")] + [switch] + $TimeSwitch, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The interval between sources + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_between")] + [timespan] + $Interval, + # The duration between sources that are switching at a time. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_duration")] + [timespan] + $Duration, -process { + # The item that will be switched in a TimeSwitch, after -Duration and -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $TimeSwitchTo = "Next", + # If set, will switch on the underlying source's media state events. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("media_state_switch")] + [switch] + $MediaStateSwitch, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The change in media state that should trigger a switch + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] + $MediaStateChange, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # When the source switcher is trigger by media end, this determines the next source that will be switched to. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $MediaSwitchTo = "Next", - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + # The name of the transition between sources. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + } + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $TransitionName, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + # The properties sent to the transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $TransitionProperty, + + # The name of the transition used to show a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $ShowTransition, -} - + # The properties sent to the show transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $ShowTransitionProperty, -} + # The transition used to hide a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + } + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") + } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $HideTransition, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSOutput { + # The properties sent to the hide transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $HideTransitionProperty, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] -[Alias('obs.powershell.websocket.ToggleOutput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -process { + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "source_switcher" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + if ($myParameters[$parameter.Name] -is [timespan]) { + $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds } } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + + $selectedIndex = -1 + $sourcesObject = @( + $currentIndex = -1 + foreach ($sourceName in $SourceList) { + $currentIndex++ + $selected = ($null -ne $Select) -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [string] -and + ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) + ) + ) + if ($selected) { + $selectedIndex = $currentIndex + } + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} + } + ) - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ($sourcesObject) { + $myParameterData['sources'] = $sourcesObject + if ($selectedIndex -gt 0) { + $myParameterData["current_index"] = $selectedIndex + } + } + elseif ($Select -is [int]) { + $myParameterData['current_index'] = $Select } + -} + + if ($TransitionName) { + if ($TransitionName -notlike '*_transition') { + $TransitionName = "${TransitionName}_transition" + } + $myParameterData["transition"] = $TransitionName + } + + if ($TransitionProperty) { + $myParameterData["transition_properties"] = $TransitionProperty + } + if ($ShowTransition) { + if ($ShowTransition -notlike '*_transition') { + $ShowTransition = "${ShowTransition}_transition" + } + $myParameterData["show_transition"] = $ShowTransition + } -} + if ($ShowTransitionProperty) { + $myParameterData["show_transition_properties"] = $ShowTransitionProperty + } - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecord { + if ($HideTransition) { + if ($HideTransition -notlike '*_transition') { + $HideTransition = "${HideTransition}_transition" + } + $myParameterData["hide_transition"] = $ShowTransition + } + if ($HideTransitionProperty) { + $myParameterData["hide_transition_properties"] = $HideTransitionProperty + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] -[Alias('obs.powershell.websocket.ToggleRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if ($TimeSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["time_switch_to"] = $vvi + break + } + } + } + if ($MediaSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_state_switch_to"] = $vvi + break + } + } + } -process { + if ($MediaStateChange) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_switch_state"] = $vvi + break + } + } + } + + if (-not $Name) { + $Name = $myParameters["Name"] = "Source Switcher" + } + - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + if ($sceneItem) { + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen + } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSVLCSource { + + + [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file','Playlist')] + [string[]] + $FilePath, + + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + # If an `[IO.FileInfo]` is provided, this will be the exact file. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." + } + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, -} + # If set, will shuffle the playlist + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shuffle")] + [switch] + $Shuffle, + # If set, the playlist will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, -} + # If set, will show subtitles, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle_enable")] + [Alias('ShowSubtitles','Subtitles')] + [switch] + $Subtitle, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecordPause { + # The selected audio track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("track")] + [int] + $AudioTrack, + # The selected subtitle track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle")] + [int] + $SubtitleTrack, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] -[Alias('obs.powershell.websocket.ToggleRecordPause')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -process { + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + $_ + + } + $InputKind = "vlc_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -} - - -} + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } + + $allPaths = @(foreach ($path in $FilePath) { + foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { + $_.Path + } + }) + $playlistObject = @( + $currentIndex = 0 + foreach ($path in $allPaths) { + $currentIndex++ + $selected = $Select -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or + ($select -is [string] -and + ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) + ) + ) + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} + } + ) + $myParameterData['playlist'] = $playlistObject -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] -[Alias('obs.powershell.websocket.ToggleReplayBuffer')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - + if (-not $Name) { + $Name = $myParameters["Name"] = $FilePathItem.Name + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } - + + } } - - -} - #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSStream { +function Set-OBSWaveformSource { + + + [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] + param( + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, + + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + # The audio source for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("audio_source")] + [string] + $AudioSource, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] -[Alias('obs.powershell.websocket.ToggleStream')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The display mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("display_mode")] + [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] + [string] + $DisplayMode, + # The render mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("line","solid","gradient")] + [string] + $RenderMode, -process { + # The windowing mode for the waveform. + # This is the mathematical function used to determine the current "window" of audio data. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("hann","hamming","blackman","blackman_harris","none")] + [string] + $WindowMode, + + # The color used for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_base")] + [PSObject] + $Color, + # The crest color used for the waveform. + # This will be ignored if the render mode is not "gradient". + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_crest")] + [PSObject] + $CrestColor, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The channel mode for the waveform. + # This can be either mono or stereo. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_mode")] + [ValidateSet("mono","stereo")] + [string] + $ChannelMode, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # The number of pixels between each channel in stereo mode + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_spacing")] + [int] + $ChannelSpacing, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + # If set, will use a radial layout for the waveform + # Radial layouts will ignore the desired height of the source and instead create a square. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("radial_layout")] + [switch] + $RadialLayout, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + # If set, will invert the direction for a radial waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("invert_direction")] + [switch] + $InvertRadialDirection, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # If set, will normalize the volume displayed in the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("normalize_volume")] + [switch] + $NoramlizeVolume, -} + # If set, will automatically declare an FFTSize + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("auto_fft_size")] + [switch] + $AutoFftSize, + # If set, will attempt to make audio peaks render faster. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fast_peaks")] + [switch] + $FastPeak, -} + # The width of the waveform bar. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_width")] + [int] + $BarWidth, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSVirtualCam { + # The gap between waveform bars. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_gap")] + [int] + $BarGap, + # The width of waveform bar step. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_width")] + [int] + $StepWidth, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] -[Alias('obs.powershell.websocket.ToggleVirtualCam')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The gap between waveform bar steps. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_gap")] + [int] + $StepGap, + # The low-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_low")] + [int] + $LowCutoff, -process { + # The high-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_high")] + [int] + $HighCutoff, + # The floor of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("floor")] + [int] + $Floor, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The ceiling of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ceiling")] + [int] + $Ceiling, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("slope")] + [double] + $Slope, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_q")] + [Alias('RollOffOctaves')] + [double] + $RollOffOctave, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_rate")] + [double] + $RollOffRate, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("grad_ratio")] + [double] + $GradientRatio, -} + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("deadzone")] + [double] + $Deadzone, + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("temporal_smoothing")] + [ValidateSet("none","exp_moving_avg")] + [string] + $TemporalSmoothing, -} + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSEffect -{ - - param( - # The name of the effect. + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectName')] + [Alias('InputName')] [string] - $Name - ) + $Name, - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput } - } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - process { - if (-not $Name) { - $script:OBSFX.Values - } elseif ($script:OBSFX[$name]) { - $script:OBSFX[$name] + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) } -} -#.ExternalHelp obs-powershell-Help.xml -function Import-OBSEffect { + $DynamicParameters - - param( - # The source location of the effect. - # This can be a string, file, directory, command, or module. - [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] - [Alias( - 'FromPath', - 'FromModule', - 'FromScript', - 'FromFunction', - 'FullName', - 'Path', - 'Source' - )] - [ValidateScript({ - $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." } - return $true - })] + begin { + $inputKind = "phandasm_waveform_source" + filter ToOBSColor { + + if ($_ -is [uint32]) { $_ } + elseif ($_ -is [string]) { + if ($_ -match '^\#[a-f0-9]{3,4}$') { + $_ = $_ -replace '[a-f0-9]','$0$0' + } + + if ($_ -match '^#[a-f0-9]{8}$') { + ( + '0x' + + (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') + ) -as [UInt32] + } + elseif ($_ -match '^#[a-f0-9]{6}$') { + + ( + '0xff' + + (($_ -replace '#').ToCharArray()[-1..-6] -join '') + ) -as [UInt32] + } + } + + } - $From - ) + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} - } + $MyInvocationName = "$($MyInvocation.InvocationName)" + $myVerb, $myNoun = $MyInvocationName -split '-' + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + $IsGet = $myVerb -eq "Get" + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - $newEffects = @() - $obsEffectsPattern = [Regex]::new(' - (?> - ^OBS.(?>fx|effects?)\p{P} - | - [\p{P}-[-]]OBS\.(?>fx|effects?)$ - | - \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ - ) - ','IgnoreCase,IgnorePatternWhitespace') - } + if ( + $IsGet -or + $NoVerb + ) { + $inputsOfKind = + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + if ($NonNameParameters -and -not $IsGet) { + $paramCopy = [Ordered]@{} + $PSBoundParameters + if ($paramCopy.Name) { $paramCopy.Remove('Name') } + $inputsOfKind | & $myScriptBlock @paramCopy + } else { + $inputsOfKind + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - process { - # Since -From can be many things, but a metric has to be a command, - # the purpose of this function is to essentially resolve many things to a command, - # and then cache that command. + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # If -From was a string - if ($From -is [string]) { - # It could be a module, so check those first. - :ResolveFromString do { - foreach ($loadedModule in @(Get-Module)) { - # If we find the module, don't try to resolve -From as a path - if ($loadedModule.Name -eq $from) { - # (just set -From again and let the function continue) - $from = $fromModule = $loadedModule;break ResolveFromString - } - + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - # If we think from was a path - $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) - # attempt to resolve it - if ($resolvedPath) { - $from = Get-Item -LiteralPath $resolvedPath + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } - } while ($false) + } } - # If -From is a module - if ($from -is [Management.Automation.PSModuleInfo]) { - # recursively call ourselves with all matching commands - @($from.ExportedCommands.Values) -match $obsEffectsPattern | - Import-OBSEffect - # then, make -From a directory - if ($from.Path) { - $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue - } + + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($AudioSource) { + "$($AudioSource)-Waveform" + } else { + "Waveform" + } } - # If -From is a directory - if ($from -is [IO.DirectoryInfo]) { - $FromDirectory = $from - # recursively call ourselves with all matching scripts - Get-ChildItem -LiteralPath $from.FullName -Recurse -File | - Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | - Import-OBSEffect - return + if ($myParameterData.color_base) { + $myParameterData.color_base = $myParameterData.color_base | ToOBSColor + } + + if ($myParameterData.color_crest) { + $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor } - # If -From is a file - if ($from -is [IO.FileInfo]) { - # and it matches the naming convention - if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } - # make -From a command. - $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # If -From is a command - if ($from -is [Management.Automation.CommandInfo]) { - # decorate the command - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect') + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - if ($from -is [Management.Automation.ApplicationInfo]) { - $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' - $newEffect = [PSCustomObject][Ordered]@{ - PSTypeName = 'OBS.PowerShell.Effect' - Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json - EffectName = $effectName - TypeName = $TypeName - } - $script:OBSFX[$effectName] = $newEffect - $newEffects += $newEffect - $newEffect - } else { - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } - # and add it to our list of new metrics - $newEffects+= $from - $script:OBSFX[$from.EffectName] = $from - $from - } - } - } - - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [Alias('Name')] - [string] - $EffectName - ) + } - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - } - - process { - if ($script:OBSFX[$name]) { - $script:OBSFX.Stop() - $script:OBSFX.Remove($name) + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] } + } } #.ExternalHelp obs-powershell-Help.xml -function Start-OBSEffect -{ +function Set-OBSWindowSource { - [CmdletBinding(PositionalBinding=$false)] + + [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] param( - # The name of the effect. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - $effectNames = @(Get-OBSEffect| - Select-Object -Unique -ExpandProperty EffectName) - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($effectNames -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(Mandatory)] - [string[]] - $EffectName, - - # The duration of the effect. - # If provided, all effects should use this duration. - # If not provided, each effect should use it's own duration. - [Timespan] - $Duration, - - # The parameters passed to the effect. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectParameters')] - [Collections.IDictionary] - $EffectParameter = @{}, - - # The arguments passed to the effect. - [Parameter(ValueFromRemainingArguments)] - [Alias('EffectArguments')] - [PSObject[]] - $EffectArgument = @(), - - # If provided, will step thru running + # The monitor number. + # This the number of the monitor you would like to capture. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ticks')] - [int] - $Step, + [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] + [string] + $WindowTitle, - # The SceneItemID. If this is provided, the effect will be given a target. + # The number of the capture method. By default, automatic (0). [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("method")] [int] - $SceneItemID, + $CaptureMethod, - # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. + # The capture priority. [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet('ExactMatch','SameType','SameExecutable')] [string] - $SceneName, + $CapturePriority, - # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. [Parameter(ValueFromPipelineByPropertyName)] - [string] - $FilterName, + [ComponentModel.DefaultBindingProperty("cursor")] + [switch] + $CaptureCursor, - # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. + # If set, will capture the client area. + # This will be set by default. [Parameter(ValueFromPipelineByPropertyName)] - [string] - $SourceName, + [ComponentModel.DefaultBindingProperty("client_area")] + [switch] + $ClientArea, - # If set, will loop the effect. + # If set, will force SDR. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("force_sdr")] [switch] - $Loop, + $ForceSDR, - # If provided, will loop the effect a number of times. - [int] - $LoopCount, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - # If set, will bounce the effect (flip it / reverse it) - [switch] - $Bounce, + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, - # If set, will reverse an effect. + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] [switch] - $Reverse + $Force ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - process { - foreach ($NameOfEffect in $EffectName) { - $obsEffect = Get-OBSEffect -EffectName $NameOfEffect - if (-not $obsEffect) { - Write-Warning "No Effect named '$NameOfEffect'" - continue + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if ($LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force - } + } + begin { + $InputKind = "window_capture" - if ($loop -or $Bounce) { - $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force - if (-not $LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force - } + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters } else { - $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } - if ($Reverse) { - $obsEffect.Reversed = $true + + if (-not $myParameters["WindowTitle"]) { + while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { + $_ = $_.Parent } - - if ($obsEffect -isnot [Management.Automation.CommandInfo]) { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - $obsEffect.Start() - - } else { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - if (-not $this) { - if ($_.pstypenames -like '*.GetSourceFilter*') { - $this = $_ - } elseif ($FilterName -and $SourceName) { - $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName - } - - if ($_.pstypenames -like '*.GetSceneItem*') { - $this = $_ - } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { - $this = - foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { - if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { - $sceneItem;break - } - elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { - $sceneItem;break - } - } - } - } + if ($_.MainWindowTitle) { + $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" + } + } - if ($Duration -and $obsEffect.Parameters.Duration) { - $EffectParameter.Duration = $Duration + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + if ($myParameters["WindowTitle"]) { + $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] + } + else { + $Name = "WindowCapture" + } + } + + + + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } + } - $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument - if ($obsEffectOutput) { - $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force - if ($step) { - $obsEffect.Step($step) - } else { - $obsEffect.Start() - } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } - $obsEffect } - - - } -} -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [string] - $EffectName) - process { - $obsEffect = Get-OBSEffect -EffectName $EffectName + if ($null -ne $CaptureMethod) { + $myParameterData["method"] = $CaptureMethod + } + if ($CapturePriority -eq 'ExactMatch') { + $myParameterData["priority"] = 1 + } + elseif ($CapturePriority -eq 'SameType') { + $myParameterData["priority"] = 0 + } + elseif ($CapturePriority -eq 'SameExecutable') { + $myParameterData["priority"] = 2 + } - if (-not $obsEffect) { return } + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "window_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } - $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window + foreach ($windowInfo in $possibleWindows) { + if (-not $WindowTitle) { continue } + if ( + ($windowInfo.itemName -eq $WindowTitle) -or + ($windowInfo.ItemValue -eq $WindowTitle) -or + ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or + ($windowInfo.ItemValue -like "*$WindowTitle*") -or + ($windowInfo.ItemName -like "*$WindowTitle*") + ) { + $myParameterData["window"] = $windowInfo.itemValue + break + } + } + + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return + } + + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name + } + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + } + } -} \ No newline at end of file +} From 20f088e9e40a75a0858937a62a1282238caf478c Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Sun, 17 May 2026 11:46:54 -0700 Subject: [PATCH 064/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- Shaders/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shaders/README.md b/Shaders/README.md index 194482fc..ad1a361b 100644 --- a/Shaders/README.md +++ b/Shaders/README.md @@ -1 +1,2 @@ -This directory contains some additional Pixel Shaders that are not included in [obs-shader-filter](https://github.com/exeldro/obs-shaderfilter/) \ No newline at end of file +This directory contains some additional Pixel Shaders that may not be included in +[obs-shader-filter](https://github.com/exeldro/obs-shaderfilter/) \ No newline at end of file From 1d249deda7af4c6b03f2821285c56dcf315cd0a7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:49 +0000 Subject: [PATCH 065/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- Commands/Shaders/Get-OBSAsciiShader.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/Shaders/Get-OBSAsciiShader.ps1 b/Commands/Shaders/Get-OBSAsciiShader.ps1 index 867c0cbc..9b910a98 100644 --- a/Commands/Shaders/Get-OBSAsciiShader.ps1 +++ b/Commands/Shaders/Get-OBSAsciiShader.ps1 @@ -118,8 +118,8 @@ float2 mod(float2 x, float2 y) float4 mainImage( VertData v_in ) : TARGET { - float2 iResolution = uv_size*uv_scale; - float2 pix = v_in.pos.xy; + float2 iResolution = uv_size; + float2 pix = v_in.uv * iResolution; float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; From d9b5cd85517346f04d1eb1146f163db142c6f932 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:50 +0000 Subject: [PATCH 066/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- Commands/Shaders/Get-OBSAudioShader.ps1 | 190 ++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 Commands/Shaders/Get-OBSAudioShader.ps1 diff --git a/Commands/Shaders/Get-OBSAudioShader.ps1 b/Commands/Shaders/Get-OBSAudioShader.ps1 new file mode 100644 index 00000000..fcfbb990 --- /dev/null +++ b/Commands/Shaders/Get-OBSAudioShader.ps1 @@ -0,0 +1,190 @@ +function Get-OBSAudioShader { + +[Alias('Set-OBSAudioShader','Add-OBSAudioShader')] +param( +# Set the audio_peak of OBSAudioShader +[Alias('audio_peak')] +[ComponentModel.DefaultBindingProperty('audio_peak')] +[Single] +$AudioPeak, +# Set the audio_magnitude of OBSAudioShader +[Alias('audio_magnitude')] +[ComponentModel.DefaultBindingProperty('audio_magnitude')] +[Single] +$AudioMagnitude, +# Set the intensity of OBSAudioShader +[ComponentModel.DefaultBindingProperty('intensity')] +[Single] +$Intensity, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'audio' +$ShaderNoun = 'OBSAudioShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Audio shader example showing the difference between audio_peak and audio_magnitude. +// Left half uses audio_peak (red), right half uses audio_magnitude (blue). +uniform float audio_peak; +uniform float audio_magnitude; + +uniform float intensity < + string label = "Audio intensity"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 3.0; + float step = 0.1; +> = 1.0; + +float4 mainImage(VertData v_in) : TARGET { + float4 color = image.Sample(textureSampler, v_in.uv); + + // Split screen based on UV coordinate + if (v_in.uv.x < 0.5) { + // Left half: audio_peak (instantaneous spikes, more reactive) + // Tint with red to show peak activity. + float peak_strength = audio_peak * intensity; + float3 peak_color = color.rgb + float3(peak_strength, 0, 0); + return float4(peak_color, color.a); + } else { + // Right half: audio_magnitude (RMS/averaged levels, smoother) + // Tint with blue to show magnitude activity. + float mag_strength = audio_magnitude * intensity; + float3 mag_color = color.rgb + float3(0, 0, mag_strength); + return float4(mag_color, color.a); + } +} + +/* +EXPLANATION: +- audio_peak: Shows instantaneous maximum levels, very responsive to drums/percussion. +- audio_magnitude: Shows RMS (Root Mean Square) levels, smoother and represents sustained audio. + +TYPICAL BEHAVIOR: +- With music containing drums: Left side (peak) will flash more dramatically on beats. +- With sustained tones: Right side (magnitude) will show more consistent levels. +- Peak reacts faster to sudden sounds, magnitude is more stable for smooth effects. +*/ + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 365b63ac8cb4874c58df8e796b6cb11ed1930b60 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:50 +0000 Subject: [PATCH 067/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...OBSDisplacementMapAdvancedInvertShader.ps1 | 433 ++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 Commands/Shaders/Get-OBSDisplacementMapAdvancedInvertShader.ps1 diff --git a/Commands/Shaders/Get-OBSDisplacementMapAdvancedInvertShader.ps1 b/Commands/Shaders/Get-OBSDisplacementMapAdvancedInvertShader.ps1 new file mode 100644 index 00000000..de6b610a --- /dev/null +++ b/Commands/Shaders/Get-OBSDisplacementMapAdvancedInvertShader.ps1 @@ -0,0 +1,433 @@ +function Get-OBSDisplacementMapAdvancedInvertShader { + +[Alias('Set-OBSDisplacementMapAdvancedInvertShader','Add-OBSDisplacementMapAdvancedInvertShader')] +param( +# Set the displacement_info of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the displacement_curve of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_curve')] +[ComponentModel.DefaultBindingProperty('displacement_curve')] +[Int32] +$DisplacementCurve, +# Set the blur_info of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_info')] +[ComponentModel.DefaultBindingProperty('blur_info')] +[String] +$BlurInfo, +# Set the blur_size of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_size')] +[ComponentModel.DefaultBindingProperty('blur_size')] +[Single] +$BlurSize, +# Set the blur_quality of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_quality')] +[ComponentModel.DefaultBindingProperty('blur_quality')] +[Single] +$BlurQuality, +# Set the blur_directions of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_directions')] +[ComponentModel.DefaultBindingProperty('blur_directions')] +[Single] +$BlurDirections, +# Set the blur_angle of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_angle')] +[ComponentModel.DefaultBindingProperty('blur_angle')] +[Single] +$BlurAngle, +# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedInvertShader +[Alias('chromatic_aberration_info')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] +[String] +$ChromaticAberrationInfo, +# Set the chromatic_aberration of OBSDisplacementMapAdvancedInvertShader +[Alias('chromatic_aberration')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration')] +[Single] +$ChromaticAberration, +# Set the colorize_info of OBSDisplacementMapAdvancedInvertShader +[Alias('colorize_info')] +[ComponentModel.DefaultBindingProperty('colorize_info')] +[String] +$ColorizeInfo, +# Set the colorize_color of OBSDisplacementMapAdvancedInvertShader +[Alias('colorize_color')] +[ComponentModel.DefaultBindingProperty('colorize_color')] +[String] +$ColorizeColor, +# Set the flags_info of OBSDisplacementMapAdvancedInvertShader +[Alias('flags_info')] +[ComponentModel.DefaultBindingProperty('flags_info')] +[String] +$FlagsInfo, +# Set the blue_affects_strength of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_strength')] +[ComponentModel.DefaultBindingProperty('blue_affects_strength')] +[Management.Automation.SwitchParameter] +$BlueAffectsStrength, +# Set the blue_affects_colorize of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_colorize')] +[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] +[Management.Automation.SwitchParameter] +$BlueAffectsColorize, +# Set the blue_affects_blur of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_blur')] +[ComponentModel.DefaultBindingProperty('blue_affects_blur')] +[Management.Automation.SwitchParameter] +$BlueAffectsBlur, +# Set the alpha_affects_strength of OBSDisplacementMapAdvancedInvertShader +[Alias('alpha_affects_strength')] +[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] +[Management.Automation.SwitchParameter] +$AlphaAffectsStrength, +# Set the apply_alpha of OBSDisplacementMapAdvancedInvertShader +[Alias('apply_alpha')] +[ComponentModel.DefaultBindingProperty('apply_alpha')] +[Management.Automation.SwitchParameter] +$ApplyAlpha, +# Set the background_layer of OBSDisplacementMapAdvancedInvertShader +[Alias('background_layer')] +[ComponentModel.DefaultBindingProperty('background_layer')] +[String] +$BackgroundLayer, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'displacement_map_advanced_invert' +$ShaderNoun = 'OBSDisplacementMapAdvancedInvertShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; +> = 0; + +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) + +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; + +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; + +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; + +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; + +uniform texture2d background_layer < + string label = "Background Layer"; +>; + +float4 mainImage(VertData v_in) : TARGET +{ + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; + + float4 map_rgba = image.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; + } + + if (chromatic_aberration) { + float4 sc_r = background_layer.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_g = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_b = background_layer.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + + sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + } else { + sc = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + } + + transparent += sc.a; + count++; + base_rgba += sc * sc.a; + samples += sc.a; + } + } + + //Calculate averages + if (samples > 0.0) + base_rgba /= samples; + + base_rgba.a = transparent / count; + } + + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + + if (apply_alpha) { + float4 background_rgba = background_layer.Sample(textureSampler, v_in.uv); + + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 0c4241bc6f5e682b4d8fdfe1dae09d826748da78 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:50 +0000 Subject: [PATCH 068/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSDisplacementMapAdvancedShader.ps1 | 433 ++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 Commands/Shaders/Get-OBSDisplacementMapAdvancedShader.ps1 diff --git a/Commands/Shaders/Get-OBSDisplacementMapAdvancedShader.ps1 b/Commands/Shaders/Get-OBSDisplacementMapAdvancedShader.ps1 new file mode 100644 index 00000000..294110d0 --- /dev/null +++ b/Commands/Shaders/Get-OBSDisplacementMapAdvancedShader.ps1 @@ -0,0 +1,433 @@ +function Get-OBSDisplacementMapAdvancedShader { + +[Alias('Set-OBSDisplacementMapAdvancedShader','Add-OBSDisplacementMapAdvancedShader')] +param( +# Set the displacement_info of OBSDisplacementMapAdvancedShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapAdvancedShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapAdvancedShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the displacement_curve of OBSDisplacementMapAdvancedShader +[Alias('displacement_curve')] +[ComponentModel.DefaultBindingProperty('displacement_curve')] +[Int32] +$DisplacementCurve, +# Set the blur_info of OBSDisplacementMapAdvancedShader +[Alias('blur_info')] +[ComponentModel.DefaultBindingProperty('blur_info')] +[String] +$BlurInfo, +# Set the blur_size of OBSDisplacementMapAdvancedShader +[Alias('blur_size')] +[ComponentModel.DefaultBindingProperty('blur_size')] +[Single] +$BlurSize, +# Set the blur_quality of OBSDisplacementMapAdvancedShader +[Alias('blur_quality')] +[ComponentModel.DefaultBindingProperty('blur_quality')] +[Single] +$BlurQuality, +# Set the blur_directions of OBSDisplacementMapAdvancedShader +[Alias('blur_directions')] +[ComponentModel.DefaultBindingProperty('blur_directions')] +[Single] +$BlurDirections, +# Set the blur_angle of OBSDisplacementMapAdvancedShader +[Alias('blur_angle')] +[ComponentModel.DefaultBindingProperty('blur_angle')] +[Single] +$BlurAngle, +# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration_info')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] +[String] +$ChromaticAberrationInfo, +# Set the chromatic_aberration of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration')] +[Single] +$ChromaticAberration, +# Set the colorize_info of OBSDisplacementMapAdvancedShader +[Alias('colorize_info')] +[ComponentModel.DefaultBindingProperty('colorize_info')] +[String] +$ColorizeInfo, +# Set the colorize_color of OBSDisplacementMapAdvancedShader +[Alias('colorize_color')] +[ComponentModel.DefaultBindingProperty('colorize_color')] +[String] +$ColorizeColor, +# Set the flags_info of OBSDisplacementMapAdvancedShader +[Alias('flags_info')] +[ComponentModel.DefaultBindingProperty('flags_info')] +[String] +$FlagsInfo, +# Set the blue_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_strength')] +[ComponentModel.DefaultBindingProperty('blue_affects_strength')] +[Management.Automation.SwitchParameter] +$BlueAffectsStrength, +# Set the blue_affects_colorize of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_colorize')] +[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] +[Management.Automation.SwitchParameter] +$BlueAffectsColorize, +# Set the blue_affects_blur of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_blur')] +[ComponentModel.DefaultBindingProperty('blue_affects_blur')] +[Management.Automation.SwitchParameter] +$BlueAffectsBlur, +# Set the alpha_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('alpha_affects_strength')] +[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] +[Management.Automation.SwitchParameter] +$AlphaAffectsStrength, +# Set the apply_alpha of OBSDisplacementMapAdvancedShader +[Alias('apply_alpha')] +[ComponentModel.DefaultBindingProperty('apply_alpha')] +[Management.Automation.SwitchParameter] +$ApplyAlpha, +# Set the mask_layer of OBSDisplacementMapAdvancedShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] +[String] +$MaskLayer, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'displacement_map_advanced' +$ShaderNoun = 'OBSDisplacementMapAdvancedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; +> = 0; + +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) + +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; + +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; + +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; + +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; + +uniform texture2d mask_layer < + string label = "Mask Layer"; +>; + +float4 mainImage(VertData v_in) : TARGET +{ + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; + + float4 map_rgba = mask_layer.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; + } + + if (chromatic_aberration) { + float4 sc_r = image.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_g = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_b = image.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + + sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + } else { + sc = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + } + + transparent += sc.a; + count++; + base_rgba += sc * sc.a; + samples += sc.a; + } + } + + //Calculate averages + if (samples > 0.0) + base_rgba /= samples; + + base_rgba.a = transparent / count; + } + + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + + if (apply_alpha) { + float4 background_rgba = image.Sample(textureSampler, v_in.uv); + + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From ab2253aaaf8dc18200912085f00fad0acdc835ee Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:50 +0000 Subject: [PATCH 069/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSDisplacementMapInvertShader.ps1 | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 Commands/Shaders/Get-OBSDisplacementMapInvertShader.ps1 diff --git a/Commands/Shaders/Get-OBSDisplacementMapInvertShader.ps1 b/Commands/Shaders/Get-OBSDisplacementMapInvertShader.ps1 new file mode 100644 index 00000000..9408f708 --- /dev/null +++ b/Commands/Shaders/Get-OBSDisplacementMapInvertShader.ps1 @@ -0,0 +1,187 @@ +function Get-OBSDisplacementMapInvertShader { + +[Alias('Set-OBSDisplacementMapInvertShader','Add-OBSDisplacementMapInvertShader')] +param( +# Set the displacement_info of OBSDisplacementMapInvertShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapInvertShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapInvertShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the background_layer of OBSDisplacementMapInvertShader +[Alias('background_layer')] +[ComponentModel.DefaultBindingProperty('background_layer')] +[String] +$BackgroundLayer, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'displacement_map_invert' +$ShaderNoun = 'OBSDisplacementMapInvertShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform texture2d background_layer ; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 map = image.Sample(textureSampler, v_in.uv); + float4 base = background_layer.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = background_layer.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From ca58f57696588f33dd17cffdca9f02aef6768abb Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:50 +0000 Subject: [PATCH 070/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Shaders/Get-OBSDisplacementMapShader.ps1 | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 Commands/Shaders/Get-OBSDisplacementMapShader.ps1 diff --git a/Commands/Shaders/Get-OBSDisplacementMapShader.ps1 b/Commands/Shaders/Get-OBSDisplacementMapShader.ps1 new file mode 100644 index 00000000..b7e7f35b --- /dev/null +++ b/Commands/Shaders/Get-OBSDisplacementMapShader.ps1 @@ -0,0 +1,187 @@ +function Get-OBSDisplacementMapShader { + +[Alias('Set-OBSDisplacementMapShader','Add-OBSDisplacementMapShader')] +param( +# Set the displacement_info of OBSDisplacementMapShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the mask_layer of OBSDisplacementMapShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] +[String] +$MaskLayer, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'displacement_map' +$ShaderNoun = 'OBSDisplacementMapShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform texture2d mask_layer ; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 map = mask_layer.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = image.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From a4ec7082103e3b990619763f284e5e29601eba7e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:51 +0000 Subject: [PATCH 071/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Shaders/Get-OBSGlitchPeriodicShader.ps1 | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 Commands/Shaders/Get-OBSGlitchPeriodicShader.ps1 diff --git a/Commands/Shaders/Get-OBSGlitchPeriodicShader.ps1 b/Commands/Shaders/Get-OBSGlitchPeriodicShader.ps1 new file mode 100644 index 00000000..5818c18a --- /dev/null +++ b/Commands/Shaders/Get-OBSGlitchPeriodicShader.ps1 @@ -0,0 +1,243 @@ +function Get-OBSGlitchPeriodicShader { + +[Alias('Set-OBSGlitchPeriodicShader','Add-OBSGlitchPeriodicShader')] +param( +# Set the PERI of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('PERI')] +[Single] +$PERI, +# Set the DURA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('DURA')] +[Single] +$DURA, +# Set the AMPL of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('AMPL')] +[Single] +$AMPL, +# Set the SCRA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('SCRA')] +[Single] +$SCRA, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'glitch-periodic' +$ShaderNoun = 'OBSGlitchPeriodicShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Created by Éric Nicolas (ccjmne) for use with obs-shaderfilter 12/2025 +// Port of: https://www.shadertoy.com/view/WfVfDh +// Originally forked from: https://www.shadertoy.com/view/MtXBDs + +#define PI 3.14159265359 + +/* For visual explanation of the paramters, see */ +/* https://www.desmos.com/calculator/vezu1wyqma */ +/* */ +/* Period How often a glitch occurs (in seconds) 0–? */ +/* Duration How long a glitch lasts (in seconds) 0–Period */ +/* Amplitude How intense a glitch is 0–1 */ +/* Scratchiness How jittery a glitch is 0–1 */ + +uniform float PERI< + string label = "Period"; + string widget_type = "slider"; + float minimum = 1.; + float maximum = 60.; + float step = 1.; +> = 6.; + +uniform float DURA< + string label = "Duration"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 60.; + float step = .01; +> = .5; + +uniform float AMPL< + string label = "Amplitude"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .15; + +uniform float SCRA< + string label = "Scratchiness"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .2; + +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); +} + +float randomRange(in float2 seed, in float lo, in float hi) { + return lo + random2d(seed) * (hi - lo); +} + +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); +} + +float4 mainImage(VertData v_in): TARGET { + float time = floor(elapsed_time * SCRA * 60.); + float2 uv = v_in.uv; + + // Periodic intermittence + float AMP = AMPL * (cos(2. * PI * max(0., (mod(-elapsed_time / PERI, 1.) - 1.) * PERI / DURA + 1.)) * -.5 + .5); + + float4 outCol = image.Sample(textureSampler, uv); + + // Randomly offset slices horizontally + float offsetMax = AMP / 2.; + for (float i = 0.; i < 10. * AMP; i += 1.) { + float sliceY = random2d( float2(time, 2345. + i)); + float sliceH = random2d( float2(time, 9035. + i)) * .25; + float offsetH = randomRange(float2(time, 9625. + i), -offsetMax, offsetMax); + float2 uvOff = uv; + uvOff.x += offsetH; + if (insideRange(uv.y, sliceY, frac(sliceY + sliceH)) == 1.) { + outCol = image.Sample(textureSampler, uvOff); + } + } + + // Slightly offset one entire channel + offsetMax = AMP / 6.; + float2 colOff = float2( + randomRange(float2(time, 9545.), -offsetMax, offsetMax), + randomRange(float2(time, 7205.), -offsetMax, offsetMax) + ); + float rnd = random2d(float2(time , 9545.)); + if (rnd < .33) outCol.r = image.Sample(textureSampler, uv + colOff).r; + else if (rnd < .66) outCol.g = image.Sample(textureSampler, uv + colOff).g; + else outCol.b = image.Sample(textureSampler, uv + colOff).b; + + return outCol; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 1d4d849d3d7874662d5222b430ce82a45cbcc608 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:51 +0000 Subject: [PATCH 072/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- Commands/Shaders/Get-OBSNoiseShader.ps1 | 210 ++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 Commands/Shaders/Get-OBSNoiseShader.ps1 diff --git a/Commands/Shaders/Get-OBSNoiseShader.ps1 b/Commands/Shaders/Get-OBSNoiseShader.ps1 new file mode 100644 index 00000000..3cfb0f07 --- /dev/null +++ b/Commands/Shaders/Get-OBSNoiseShader.ps1 @@ -0,0 +1,210 @@ +function Get-OBSNoiseShader { + +[Alias('Set-OBSNoiseShader','Add-OBSNoiseShader')] +param( +# Set the speed of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the scale of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the noiseLevel of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('noiseLevel')] +[Single] +$NoiseLevel, +# Set the monochromatic of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('monochromatic')] +[Management.Automation.SwitchParameter] +$Monochromatic, +# Set the use_rand of OBSNoiseShader +[Alias('use_rand')] +[ComponentModel.DefaultBindingProperty('use_rand')] +[Management.Automation.SwitchParameter] +$UseRand, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'noise' +$ShaderNoun = 'OBSNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100; + float step = 0.1; +> = 1; +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 10; + float step = 0.0001; +> = 6; +uniform float noiseLevel< + string label = "Noise Level"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 1; + float step = 0.001; +> = 1; +uniform bool monochromatic = false; +uniform bool use_rand = false; + +float rand(float2 st) +{ + return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float time = rand_activation_f + (speed / 60) * elapsed_time * 0.00001; + + if (use_rand) { + time = rand_f; + } + + float4 x; + + if (monochromatic) { + x = rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + scale); + } else { + x = float4( + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 1 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 2 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 3 * scale), + 1 + ); + } + + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float3 output = lerp(rgba, x, noiseLevel); + + return float4(output.r, output.g, output.b, rgba.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 89f12bcb02e2e68c90dff90ce5294bf69c3e0818 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:51 +0000 Subject: [PATCH 073/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- Commands/Shaders/Get-OBSNormalMapShader.ps1 | 254 ++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 Commands/Shaders/Get-OBSNormalMapShader.ps1 diff --git a/Commands/Shaders/Get-OBSNormalMapShader.ps1 b/Commands/Shaders/Get-OBSNormalMapShader.ps1 new file mode 100644 index 00000000..938d1454 --- /dev/null +++ b/Commands/Shaders/Get-OBSNormalMapShader.ps1 @@ -0,0 +1,254 @@ +function Get-OBSNormalMapShader { + +[Alias('Set-OBSNormalMapShader','Add-OBSNormalMapShader')] +param( +# Set the strength of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the offsetHeight of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('offsetHeight')] +[Management.Automation.SwitchParameter] +$OffsetHeight, +# Set the invertR of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertR')] +[Management.Automation.SwitchParameter] +$InvertR, +# Set the invertG of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertG')] +[Management.Automation.SwitchParameter] +$InvertG, +# Set the invertH of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertH')] +[Management.Automation.SwitchParameter] +$InvertH, +# Set the type of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('type')] +[Int32] +$Type, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'normal_map' +$ShaderNoun = 'OBSNormalMapShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Normal map shader based on cpetry''s NormalMap-Online website +// https://github.com/cpetry/NormalMap-Online/blob/gh-pages/javascripts/shader/NormalMapShader.js + +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.001; + float maximum = 10; + float step = 0.001; +> = 1; + +uniform bool offsetHeight = true; +uniform bool invertR = false; +uniform bool invertG = false; +uniform bool invertH = false; + +uniform int type< + string label = "Filter Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Sobel"; + int option_1_value = 1; + string option_1_label = "Scharr"; +> = 0; + +float4 mainImage( VertData v_in ) : TARGET { + float2 step = float2(1.0, 1.0) / uv_size; + float2 vUv = v_in.uv; + + float dz = 1 / strength; + float dz2 = dz * dz; + + float2 tlv = float2(vUv.x - step.x, vUv.y + step.y); + float2 lv = float2(vUv.x - step.x, vUv.y ); + float2 blv = float2(vUv.x - step.x, vUv.y - step.y); + float2 tv = float2(vUv.x , vUv.y + step.y); + float2 bv = float2(vUv.x , vUv.y - step.y); + float2 trv = float2(vUv.x + step.x, vUv.y + step.y); + float2 rv = float2(vUv.x + step.x, vUv.y ); + float2 brv = float2(vUv.x + step.x, vUv.y - step.y); + + tlv = float2(tlv.x >= 0.0 ? tlv.x : (1.0 + tlv.x), tlv.y >= 0.0 ? tlv.y : (1.0 + tlv.y)); + tlv = float2(tlv.x < 1.0 ? tlv.x : (tlv.x - 1.0 ), tlv.y < 1.0 ? tlv.y : (tlv.y - 1.0 )); + lv = float2( lv.x >= 0.0 ? lv.x : (1.0 + lv.x), lv.y >= 0.0 ? lv.y : (1.0 + lv.y)); + lv = float2( lv.x < 1.0 ? lv.x : ( lv.x - 1.0 ), lv.y < 1.0 ? lv.y : ( lv.y - 1.0 )); + blv = float2(blv.x >= 0.0 ? blv.x : (1.0 + blv.x), blv.y >= 0.0 ? blv.y : (1.0 + blv.y)); + blv = float2(blv.x < 1.0 ? blv.x : (blv.x - 1.0 ), blv.y < 1.0 ? blv.y : (blv.y - 1.0 )); + tv = float2( tv.x >= 0.0 ? tv.x : (1.0 + tv.x), tv.y >= 0.0 ? tv.y : (1.0 + tv.y)); + tv = float2( tv.x < 1.0 ? tv.x : ( tv.x - 1.0 ), tv.y < 1.0 ? tv.y : ( tv.y - 1.0 )); + bv = float2( bv.x >= 0.0 ? bv.x : (1.0 + bv.x), bv.y >= 0.0 ? bv.y : (1.0 + bv.y)); + bv = float2( bv.x < 1.0 ? bv.x : ( bv.x - 1.0 ), bv.y < 1.0 ? bv.y : ( bv.y - 1.0 )); + trv = float2(trv.x >= 0.0 ? trv.x : (1.0 + trv.x), trv.y >= 0.0 ? trv.y : (1.0 + trv.y)); + trv = float2(trv.x < 1.0 ? trv.x : (trv.x - 1.0 ), trv.y < 1.0 ? trv.y : (trv.y - 1.0 )); + rv = float2( rv.x >= 0.0 ? rv.x : (1.0 + rv.x), rv.y >= 0.0 ? rv.y : (1.0 + rv.y)); + rv = float2( rv.x < 1.0 ? rv.x : ( rv.x - 1.0 ), rv.y < 1.0 ? rv.y : ( rv.y - 1.0 )); + brv = float2(brv.x >= 0.0 ? brv.x : (1.0 + brv.x), brv.y >= 0.0 ? brv.y : (1.0 + brv.y)); + brv = float2(brv.x < 1.0 ? brv.x : (brv.x - 1.0 ), brv.y < 1.0 ? brv.y : (brv.y - 1.0 )); + + float tl = image.Sample(textureSampler, tlv).r; + float l = image.Sample(textureSampler, lv ).r; + float bl = image.Sample(textureSampler, blv).r; + float t = image.Sample(textureSampler, tv ).r; + float b = image.Sample(textureSampler, bv ).r; + float tr = image.Sample(textureSampler, trv).r; + float r = image.Sample(textureSampler, rv ).r; + float br = image.Sample(textureSampler, brv).r; + + float dx = 0.0; + float dy = 0.0; + + if(type == 0) { // Sobel + dx = tl + l*2.0 + bl - tr - r*2.0 - br; + dy = tl + t*2.0 + tr - bl - b*2.0 - br; + } + else { // Scharr + dx = tl*3.0 + l*10.0 + bl*3.0 - tr*3.0 - r*10.0 - br*3.0; + dy = tl*3.0 + t*10.0 + tr*3.0 - bl*3.0 - b*10.0 - br*3.0; + } + + float invH = invertH ? -1. : 1.; + float invR = invertR ? -1. : 1.; + float invG = invertG ? -1. : 1.; + + float4 normal = float4( + float3(dx * invR * invH, dy * invG * invH, dz), + image.Sample(textureSampler, vUv).a + ); + + l = sqrt((dx * dx) + (dy * dy) + dz2); + + if (offsetHeight) { + return float4(normal.xy / l * 0.5 + 0.5, normal.zw); + } + + return float4(normal.xyz / l * 0.5 + 0.5, normal.w); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From dae7189a86d52d0cf8a18470908f9aea00bfc7ab Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:51 +0000 Subject: [PATCH 074/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSQuadrilateralCropShader.ps1 | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 Commands/Shaders/Get-OBSQuadrilateralCropShader.ps1 diff --git a/Commands/Shaders/Get-OBSQuadrilateralCropShader.ps1 b/Commands/Shaders/Get-OBSQuadrilateralCropShader.ps1 new file mode 100644 index 00000000..ef666664 --- /dev/null +++ b/Commands/Shaders/Get-OBSQuadrilateralCropShader.ps1 @@ -0,0 +1,247 @@ +function Get-OBSQuadrilateralCropShader { + +[Alias('Set-OBSQuadrilateralCropShader','Add-OBSQuadrilateralCropShader')] +param( +# Set the Top_Left_X of OBSQuadrilateralCropShader +[Alias('Top_Left_X')] +[ComponentModel.DefaultBindingProperty('Top_Left_X')] +[Single] +$TopLeftX, +# Set the Top_Left_Y of OBSQuadrilateralCropShader +[Alias('Top_Left_Y')] +[ComponentModel.DefaultBindingProperty('Top_Left_Y')] +[Single] +$TopLeftY, +# Set the Top_Right_X of OBSQuadrilateralCropShader +[Alias('Top_Right_X')] +[ComponentModel.DefaultBindingProperty('Top_Right_X')] +[Single] +$TopRightX, +# Set the Top_Right_Y of OBSQuadrilateralCropShader +[Alias('Top_Right_Y')] +[ComponentModel.DefaultBindingProperty('Top_Right_Y')] +[Single] +$TopRightY, +# Set the Bottom_Left_X of OBSQuadrilateralCropShader +[Alias('Bottom_Left_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] +[Single] +$BottomLeftX, +# Set the Bottom_Left_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Left_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +[Single] +$BottomLeftY, +# Set the Bottom_Right_X of OBSQuadrilateralCropShader +[Alias('Bottom_Right_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +[Single] +$BottomRightX, +# Set the Bottom_Right_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Right_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +[Single] +$BottomRightY, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'quadrilateral_crop' +$ShaderNoun = 'OBSQuadrilateralCropShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Quadrilateral Crop shader (inverse of a corner pin): transform a 4 points polygon to the corners of the source. +// Useful to revert perspective. + +uniform float Top_Left_X< + string label = "Top Left X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Left_Y< + string label = "Top Left Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Right_X< + string label = "Top Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Top_Right_Y< + string label = "Top Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_X< + string label = "Bottom Left X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_Y< + string label = "Bottom Left Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; + +float4 mainImage( VertData v_in ) : TARGET { + + float2 tl = float2(Top_Left_X, Top_Left_Y) * .01; + float2 tr = float2(Top_Right_X, Top_Right_Y) * .01; + float2 bl = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 br = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + float2 t = lerp(tl, tr, v_in.uv[0]); + float2 b = lerp(bl, br, v_in.uv[0]); + float2 uv = lerp(t, b, v_in.uv[1]); + + return image.Sample(textureSampler, uv); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 40396b8e847bfce3679cd238342a8793f2b02b10 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:52 +0000 Subject: [PATCH 075/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSZoomBlurTransitionShader.ps1 | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 Commands/Shaders/Get-OBSZoomBlurTransitionShader.ps1 diff --git a/Commands/Shaders/Get-OBSZoomBlurTransitionShader.ps1 b/Commands/Shaders/Get-OBSZoomBlurTransitionShader.ps1 new file mode 100644 index 00000000..08dbcfc8 --- /dev/null +++ b/Commands/Shaders/Get-OBSZoomBlurTransitionShader.ps1 @@ -0,0 +1,229 @@ +function Get-OBSZoomBlurTransitionShader { + +[Alias('Set-OBSZoomBlurTransitionShader','Add-OBSZoomBlurTransitionShader')] +param( +# Set the image_a of OBSZoomBlurTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSZoomBlurTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSZoomBlurTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSZoomBlurTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the strength of OBSZoomBlurTransitionShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'zoom_blur_transition' +$ShaderNoun = 'OBSZoomBlurTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/Ml3XR2 + +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; + +//modified zoom blur from http://transitions.glsl.io/transition/b86b90161503a0023231 +uniform float strength< + string label = "Strength (0.3)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.50; + float step = 0.01; +> = 0.3; +#define PI 3.141592653589793 + +float Linear_ease(in float begin, in float change, in float duration, in float time) { + return change * time / duration + begin; +} + +float Exponential_easeInOut(in float begin, in float change, in float duration, in float time) { + if (time == 0.0) + return begin; + else if (time == duration) + return begin + change; + time = time / (duration / 2.0); + if (time < 1.0) + return change / 2.0 * pow(2.0, 10.0 * (time - 1.0)) + begin; + return change / 2.0 * (-pow(2.0, -10.0 * (time - 1.0)) + 2.0) + begin; +} + +float Sinusoidal_easeInOut(in float begin, in float change, in float duration, in float time) { + return -change / 2.0 * (cos(PI * time / duration) - 1.0) + begin; +} + +float random(in float3 scale, in float seed) { + return frac(sin(dot(float3(seed, seed, seed), scale)) * 43758.5453 + seed); +} + +float3 crossFade(in float2 uv, in float dissolve) { + return lerp(image_a.Sample(textureSampler, uv).rgb, image_b.Sample(textureSampler, uv).rgb, dissolve); +} + +float4 mainImage(VertData v_in) : TARGET { + float2 texCoord = v_in.uv; + float progress = transition_time; + // Linear interpolate center across center half of the image + float2 center = float2(Linear_ease(0.5, 0.0, 1.0, progress),0.5); + float dissolve = Exponential_easeInOut(0.0, 1.0, 1.0, progress); + + // Mirrored sinusoidal loop. 0->strength then strength->0 + float strength2 = Sinusoidal_easeInOut(0.0, strength, 0.5, progress); + + float3 color = float3(0.0,0.0,0.0); + float total = 0.0; + float2 toCenter = center - texCoord; + + /* randomize the lookup values to hide the fixed float of samples */ + float offset = random(float3(12.9898, 78.233, 151.7182), 0.0)*0.5; + + for (float t = 0.0; t <= 20.0; t++) { + float percent = (t + offset) / 20.0; + float weight = 1.0 * (percent - percent * percent); + color += crossFade(texCoord + toCenter * percent * strength2, dissolve) * weight; + total += weight; + } + float4 rgba = float4(color / total, 1.0); + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + From 726f74fd65bd634f03e8713fb0eaeb21eaa0951b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:53 +0000 Subject: [PATCH 076/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- en-us/obs-powershell-commands.help.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/en-us/obs-powershell-commands.help.txt b/en-us/obs-powershell-commands.help.txt index da1a78af..d3a89177 100644 --- a/en-us/obs-powershell-commands.help.txt +++ b/en-us/obs-powershell-commands.help.txt @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 787 commands -(316 functions and 471 aliases) +obs-powershell exports 808 commands +(323 functions and 485 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](docs/obs-powershell-websocket-commands.md). @@ -25,6 +25,7 @@ Functions |[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | |[Disconnect-OBS](docs/Disconnect-OBS.md) |Disconnects OBS | |[Get-OBS](docs/Get-OBS.md) |Gets OBS | +|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | |[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](docs/Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | @@ -53,6 +54,7 @@ Functions |[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | |[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | |[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | |[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | @@ -106,6 +108,7 @@ Functions |[Get-OBSGroup](docs/Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | |[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | |[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | |[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | |[Get-OBSHotkey](docs/Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | @@ -131,6 +134,7 @@ Functions |[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | |[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | |[Get-OBSMonitor](docs/Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | +|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | |[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | @@ -141,6 +145,7 @@ Functions |[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | |[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | +|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | |[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | @@ -155,6 +160,7 @@ Functions |[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | |[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | |[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | @@ -220,6 +226,7 @@ Functions |[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | |[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | |[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | |[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | @@ -350,6 +357,7 @@ Aliases |[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) | |[Disconnect-OBS](docs/Disconnect-OBS.md) | |[Get-OBS](docs/Get-OBS.md) | +|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | |[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](docs/Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | @@ -378,6 +386,7 @@ Aliases |[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | |[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) | |[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) | |[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) | @@ -431,6 +440,7 @@ Aliases |[Get-OBSGroup](docs/Get-OBSGroup.md) | |[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) | |[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | |[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | |[Get-OBSHotkey](docs/Get-OBSHotkey.md) | @@ -456,6 +466,7 @@ Aliases |[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | |[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) | |[Get-OBSMonitor](docs/Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | |[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | @@ -466,6 +477,7 @@ Aliases |[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | |[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | |[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | @@ -480,6 +492,7 @@ Aliases |[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | |[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | |[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) | @@ -545,6 +558,7 @@ Aliases |[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | |[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) | |[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | |[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | From c3d0bc2203d9e412ad2058769f945ebbc13606da Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:53:53 +0000 Subject: [PATCH 077/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- obs-powershell.psd1 | 302 +++++++++++++++++++++++--------------------- 1 file changed, 156 insertions(+), 146 deletions(-) diff --git a/obs-powershell.psd1 b/obs-powershell.psd1 index a47a2bcc..fe4adfbb 100644 --- a/obs-powershell.psd1 +++ b/obs-powershell.psd1 @@ -104,11 +104,6 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Send-OBS', 'Show-OBS', 'Watch-OBS', -'Get-OBSEffect', -'Import-OBSEffect', -'Remove-OBSEffect', -'Start-OBSEffect', -'Stop-OBSEffect', 'Set-OBS3DFilter', 'Set-OBSColorFilter', 'Set-OBSEqualizerFilter', @@ -118,146 +113,6 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Set-OBSScrollFilter', 'Set-OBSShaderFilter', 'Set-OBSSharpnessFilter', -'Add-OBSInput', -'Add-OBSProfile', -'Add-OBSScene', -'Add-OBSSceneCollection', -'Add-OBSSceneItem', -'Add-OBSSourceFilter', -'Copy-OBSSceneItem', -'Get-OBSCurrentPreviewScene', -'Get-OBSCurrentProgramScene', -'Get-OBSCurrentSceneTransition', -'Get-OBSCurrentSceneTransitionCursor', -'Get-OBSGroup', -'Get-OBSGroupSceneItem', -'Get-OBSHotkey', -'Get-OBSInput', -'Get-OBSInputAudioBalance', -'Get-OBSInputAudioMonitorType', -'Get-OBSInputAudioSyncOffset', -'Get-OBSInputAudioTracks', -'Get-OBSInputDefaultSettings', -'Get-OBSInputKind', -'Get-OBSInputMute', -'Get-OBSInputPropertiesListPropertyItems', -'Get-OBSInputSettings', -'Get-OBSInputVolume', -'Get-OBSLastReplayBufferReplay', -'Get-OBSMediaInputStatus', -'Get-OBSMonitor', -'Get-OBSOutput', -'Get-OBSOutputSettings', -'Get-OBSOutputStatus', -'Get-OBSPersistentData', -'Get-OBSProfile', -'Get-OBSProfileParameter', -'Get-OBSRecordDirectory', -'Get-OBSRecordStatus', -'Get-OBSReplayBufferStatus', -'Get-OBSScene', -'Get-OBSSceneCollection', -'Get-OBSSceneItem', -'Get-OBSSceneItemBlendMode', -'Get-OBSSceneItemEnabled', -'Get-OBSSceneItemId', -'Get-OBSSceneItemIndex', -'Get-OBSSceneItemLocked', -'Get-OBSSceneItemSource', -'Get-OBSSceneItemTransform', -'Get-OBSSceneSceneTransitionOverride', -'Get-OBSSceneTransition', -'Get-OBSSourceActive', -'Get-OBSSourceFilter', -'Get-OBSSourceFilterDefaultSettings', -'Get-OBSSourceFilterKind', -'Get-OBSSourceFilterList', -'Get-OBSSourceScreenshot', -'Get-OBSSpecialInputs', -'Get-OBSStats', -'Get-OBSStreamServiceSettings', -'Get-OBSStreamStatus', -'Get-OBSStudioModeEnabled', -'Get-OBSTransitionKind', -'Get-OBSVersion', -'Get-OBSVideoSettings', -'Get-OBSVirtualCamStatus', -'Open-OBSInputFiltersDialog', -'Open-OBSInputInteractDialog', -'Open-OBSInputPropertiesDialog', -'Open-OBSSourceProjector', -'Open-OBSVideoMixProjector', -'Remove-OBSInput', -'Remove-OBSProfile', -'Remove-OBSScene', -'Remove-OBSSceneItem', -'Remove-OBSSourceFilter', -'Resume-OBSRecord', -'Save-OBSReplayBuffer', -'Save-OBSSourceScreenshot', -'Send-OBSCallVendorRequest', -'Send-OBSCustomEvent', -'Send-OBSOffsetMediaInputCursor', -'Send-OBSPauseRecord', -'Send-OBSPressInputPropertiesButton', -'Send-OBSSleep', -'Send-OBSStreamCaption', -'Send-OBSTriggerHotkeyByKeySequence', -'Send-OBSTriggerHotkeyByName', -'Send-OBSTriggerMediaInputAction', -'Send-OBSTriggerStudioModeTransition', -'Set-OBSCurrentPreviewScene', -'Set-OBSCurrentProfile', -'Set-OBSCurrentProgramScene', -'Set-OBSCurrentSceneCollection', -'Set-OBSCurrentSceneTransition', -'Set-OBSCurrentSceneTransitionDuration', -'Set-OBSCurrentSceneTransitionSettings', -'Set-OBSInputAudioBalance', -'Set-OBSInputAudioMonitorType', -'Set-OBSInputAudioSyncOffset', -'Set-OBSInputAudioTracks', -'Set-OBSInputMute', -'Set-OBSInputName', -'Set-OBSInputSettings', -'Set-OBSInputVolume', -'Set-OBSMediaInputCursor', -'Set-OBSOutputSettings', -'Set-OBSPersistentData', -'Set-OBSProfileParameter', -'Set-OBSRecordDirectory', -'Set-OBSSceneItemBlendMode', -'Set-OBSSceneItemEnabled', -'Set-OBSSceneItemIndex', -'Set-OBSSceneItemLocked', -'Set-OBSSceneItemTransform', -'Set-OBSSceneName', -'Set-OBSSceneSceneTransitionOverride', -'Set-OBSSourceFilterEnabled', -'Set-OBSSourceFilterIndex', -'Set-OBSSourceFilterName', -'Set-OBSSourceFilterSettings', -'Set-OBSStreamServiceSettings', -'Set-OBSStudioModeEnabled', -'Set-OBSTBarPosition', -'Set-OBSVideoSettings', -'Start-OBSOutput', -'Start-OBSRecord', -'Start-OBSReplayBuffer', -'Start-OBSStream', -'Start-OBSVirtualCam', -'Stop-OBSOutput', -'Stop-OBSRecord', -'Stop-OBSReplayBuffer', -'Stop-OBSStream', -'Stop-OBSVirtualCam', -'Switch-OBSInputMute', -'Switch-OBSOutput', -'Switch-OBSRecord', -'Switch-OBSRecordPause', -'Switch-OBSReplayBuffer', -'Switch-OBSStream', -'Switch-OBSVirtualCam', 'Get-OBS3dPanelShader', 'Get-OBS3dSwapTransitionShader', 'Get-OBSAddShader', @@ -267,6 +122,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSAnimatedTextureShader', 'Get-OBSAsciiShader', 'Get-OBSAspectRatioShader', +'Get-OBSAudioShader', 'Get-OBSBackgroundRemovalShader', 'Get-OBSBlendOpacityShader', 'Get-OBSBlinkShader', @@ -296,6 +152,10 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSDensitySatHueShader', 'Get-OBSDiffuseTransitionShader', 'Get-OBSDigitalRainShader', +'Get-OBSDisplacementMapAdvancedInvertShader', +'Get-OBSDisplacementMapAdvancedShader', +'Get-OBSDisplacementMapInvertShader', +'Get-OBSDisplacementMapShader', 'Get-OBSDivideRotateShader', 'Get-OBSDoodleShader', 'Get-OBSDrawingsShader', @@ -330,6 +190,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSGbCameraShader', 'Get-OBSGlassShader', 'Get-OBSGlitchAnalogShader', +'Get-OBSGlitchPeriodicShader', 'Get-OBSGlitchShader', 'Get-OBSGlowShader', 'Get-OBSGradientShader', @@ -348,6 +209,8 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSMotionBlurShader', 'Get-OBSMultiplyShader', 'Get-OBSNightSkyShader', +'Get-OBSNoiseShader', +'Get-OBSNormalMapShader', 'Get-OBSOpacityShader', 'Get-OBSPagePeelShader', 'Get-OBSPagePeelTransitionShader', @@ -358,6 +221,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSPixelationTransitionShader', 'Get-OBSPolarShader', 'Get-OBSPulseShader', +'Get-OBSQuadrilateralCropShader', 'Get-OBSRainbowShader', 'Get-OBSRainWindowShader', 'Get-OBSRectangularDropShadowShader', @@ -404,6 +268,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Get-OBSWalkingDeadPixelFixerShader', 'Get-OBSZigZagShader', 'Get-OBSZoomBlurShader', +'Get-OBSZoomBlurTransitionShader', 'Get-OBSZoomShader', 'Get-OBSZoomXYShader', 'Set-OBSAudioOutputSource', @@ -416,6 +281,151 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Set-OBSSwitchSource', 'Set-OBSVLCSource', 'Set-OBSWaveformSource', -'Set-OBSWindowSource' +'Set-OBSWindowSource', +'Add-OBSInput', +'Add-OBSProfile', +'Add-OBSScene', +'Add-OBSSceneCollection', +'Add-OBSSceneItem', +'Add-OBSSourceFilter', +'Copy-OBSSceneItem', +'Get-OBSCurrentPreviewScene', +'Get-OBSCurrentProgramScene', +'Get-OBSCurrentSceneTransition', +'Get-OBSCurrentSceneTransitionCursor', +'Get-OBSGroup', +'Get-OBSGroupSceneItem', +'Get-OBSHotkey', +'Get-OBSInput', +'Get-OBSInputAudioBalance', +'Get-OBSInputAudioMonitorType', +'Get-OBSInputAudioSyncOffset', +'Get-OBSInputAudioTracks', +'Get-OBSInputDefaultSettings', +'Get-OBSInputKind', +'Get-OBSInputMute', +'Get-OBSInputPropertiesListPropertyItems', +'Get-OBSInputSettings', +'Get-OBSInputVolume', +'Get-OBSLastReplayBufferReplay', +'Get-OBSMediaInputStatus', +'Get-OBSMonitor', +'Get-OBSOutput', +'Get-OBSOutputSettings', +'Get-OBSOutputStatus', +'Get-OBSPersistentData', +'Get-OBSProfile', +'Get-OBSProfileParameter', +'Get-OBSRecordDirectory', +'Get-OBSRecordStatus', +'Get-OBSReplayBufferStatus', +'Get-OBSScene', +'Get-OBSSceneCollection', +'Get-OBSSceneItem', +'Get-OBSSceneItemBlendMode', +'Get-OBSSceneItemEnabled', +'Get-OBSSceneItemId', +'Get-OBSSceneItemIndex', +'Get-OBSSceneItemLocked', +'Get-OBSSceneItemSource', +'Get-OBSSceneItemTransform', +'Get-OBSSceneSceneTransitionOverride', +'Get-OBSSceneTransition', +'Get-OBSSourceActive', +'Get-OBSSourceFilter', +'Get-OBSSourceFilterDefaultSettings', +'Get-OBSSourceFilterKind', +'Get-OBSSourceFilterList', +'Get-OBSSourceScreenshot', +'Get-OBSSpecialInputs', +'Get-OBSStats', +'Get-OBSStreamServiceSettings', +'Get-OBSStreamStatus', +'Get-OBSStudioModeEnabled', +'Get-OBSTransitionKind', +'Get-OBSVersion', +'Get-OBSVideoSettings', +'Get-OBSVirtualCamStatus', +'Open-OBSInputFiltersDialog', +'Open-OBSInputInteractDialog', +'Open-OBSInputPropertiesDialog', +'Open-OBSSourceProjector', +'Open-OBSVideoMixProjector', +'Remove-OBSInput', +'Remove-OBSProfile', +'Remove-OBSScene', +'Remove-OBSSceneItem', +'Remove-OBSSourceFilter', +'Resume-OBSRecord', +'Save-OBSReplayBuffer', +'Save-OBSSourceScreenshot', +'Send-OBSCallVendorRequest', +'Send-OBSCustomEvent', +'Send-OBSOffsetMediaInputCursor', +'Send-OBSPauseRecord', +'Send-OBSPressInputPropertiesButton', +'Send-OBSSleep', +'Send-OBSStreamCaption', +'Send-OBSTriggerHotkeyByKeySequence', +'Send-OBSTriggerHotkeyByName', +'Send-OBSTriggerMediaInputAction', +'Send-OBSTriggerStudioModeTransition', +'Set-OBSCurrentPreviewScene', +'Set-OBSCurrentProfile', +'Set-OBSCurrentProgramScene', +'Set-OBSCurrentSceneCollection', +'Set-OBSCurrentSceneTransition', +'Set-OBSCurrentSceneTransitionDuration', +'Set-OBSCurrentSceneTransitionSettings', +'Set-OBSInputAudioBalance', +'Set-OBSInputAudioMonitorType', +'Set-OBSInputAudioSyncOffset', +'Set-OBSInputAudioTracks', +'Set-OBSInputMute', +'Set-OBSInputName', +'Set-OBSInputSettings', +'Set-OBSInputVolume', +'Set-OBSMediaInputCursor', +'Set-OBSOutputSettings', +'Set-OBSPersistentData', +'Set-OBSProfileParameter', +'Set-OBSRecordDirectory', +'Set-OBSSceneItemBlendMode', +'Set-OBSSceneItemEnabled', +'Set-OBSSceneItemIndex', +'Set-OBSSceneItemLocked', +'Set-OBSSceneItemTransform', +'Set-OBSSceneName', +'Set-OBSSceneSceneTransitionOverride', +'Set-OBSSourceFilterEnabled', +'Set-OBSSourceFilterIndex', +'Set-OBSSourceFilterName', +'Set-OBSSourceFilterSettings', +'Set-OBSStreamServiceSettings', +'Set-OBSStudioModeEnabled', +'Set-OBSTBarPosition', +'Set-OBSVideoSettings', +'Start-OBSOutput', +'Start-OBSRecord', +'Start-OBSReplayBuffer', +'Start-OBSStream', +'Start-OBSVirtualCam', +'Stop-OBSOutput', +'Stop-OBSRecord', +'Stop-OBSReplayBuffer', +'Stop-OBSStream', +'Stop-OBSVirtualCam', +'Switch-OBSInputMute', +'Switch-OBSOutput', +'Switch-OBSRecord', +'Switch-OBSRecordPause', +'Switch-OBSReplayBuffer', +'Switch-OBSStream', +'Switch-OBSVirtualCam', +'Get-OBSEffect', +'Import-OBSEffect', +'Remove-OBSEffect', +'Start-OBSEffect', +'Stop-OBSEffect' } From 08fe19510fa9a6ce5cc8dad1a47a331ef1a196c5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:09 +0000 Subject: [PATCH 078/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBS3dPanelShader.md | 57 ++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/docs/Get-OBS3dPanelShader.md b/docs/Get-OBS3dPanelShader.md index 5079fd77..c72918bf 100644 --- a/docs/Get-OBS3dPanelShader.md +++ b/docs/Get-OBS3dPanelShader.md @@ -2,7 +2,6 @@ Get-OBS3dPanelShader -------------------- ### Synopsis - Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,121 +15,121 @@ Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [-WiggleRot ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 9fd82e96bb541290ad5b5a9c6764b35409559636 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:09 +0000 Subject: [PATCH 079/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBS3dPanelShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBS3dPanelShader.json b/docs/_data/Help/Get-OBS3dPanelShader.json index fe53f972..3e26a529 100644 --- a/docs/_data/Help/Get-OBS3dPanelShader.json +++ b/docs/_data/Help/Get-OBS3dPanelShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From ac50a6b523707c0a90718caf8cfe526e14886b12 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:11 +0000 Subject: [PATCH 080/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSAudioShader.md | 96 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/Get-OBSAudioShader.md diff --git a/docs/Get-OBSAudioShader.md b/docs/Get-OBSAudioShader.md new file mode 100644 index 00000000..627857c6 --- /dev/null +++ b/docs/Get-OBSAudioShader.md @@ -0,0 +1,96 @@ +Get-OBSAudioShader +------------------ + +### Synopsis + +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AudioMagnitude** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |1 |false |audio_magnitude| + +#### **AudioPeak** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |audio_peak| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Intensity** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +``` From d4c26035a4f4f20fa97f2cb69b16feecc49741fa Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:11 +0000 Subject: [PATCH 081/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSAudioShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSAudioShader.json diff --git a/docs/_data/Help/Get-OBSAudioShader.json b/docs/_data/Help/Get-OBSAudioShader.json new file mode 100644 index 00000000..3e439275 --- /dev/null +++ b/docs/_data/Help/Get-OBSAudioShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 8db4caceb5af96960491418fd78e6935583c1f9a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:15 +0000 Subject: [PATCH 082/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSCubeRotatingShader.md | 43 +++++++++++++------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Get-OBSCubeRotatingShader.md b/docs/Get-OBSCubeRotatingShader.md index 5c393585..ca9ab33e 100644 --- a/docs/Get-OBSCubeRotatingShader.md +++ b/docs/Get-OBSCubeRotatingShader.md @@ -2,7 +2,6 @@ Get-OBSCubeRotatingShader ------------------------- ### Synopsis - Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 3d6b8151e8b5d88cfc8e4b11e886ce5e50645e8d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:15 +0000 Subject: [PATCH 083/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSCubeRotatingShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSCubeRotatingShader.json b/docs/_data/Help/Get-OBSCubeRotatingShader.json index 8d837b62..39b20764 100644 --- a/docs/_data/Help/Get-OBSCubeRotatingShader.json +++ b/docs/_data/Help/Get-OBSCubeRotatingShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 406dfec9b78c6ffa1915c598b3a7e937b80787e6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 084/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...-OBSDisplacementMapAdvancedInvertShader.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Get-OBSDisplacementMapAdvancedInvertShader.md diff --git a/docs/Get-OBSDisplacementMapAdvancedInvertShader.md b/docs/Get-OBSDisplacementMapAdvancedInvertShader.md new file mode 100644 index 00000000..94e327ca --- /dev/null +++ b/docs/Get-OBSDisplacementMapAdvancedInvertShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedInvertShader +------------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |14 |false |background_layer| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From a07a373947f4f9da494c13b79dc6af7dcd522b19 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 085/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...BSDisplacementMapAdvancedInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json diff --git a/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json new file mode 100644 index 00000000..02d480c7 --- /dev/null +++ b/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From efa90a884397058b0e03b6ffaf3ed2eaa3c9ed5e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 086/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSDisplacementMapAdvancedShader.md | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Get-OBSDisplacementMapAdvancedShader.md diff --git a/docs/Get-OBSDisplacementMapAdvancedShader.md b/docs/Get-OBSDisplacementMapAdvancedShader.md new file mode 100644 index 00000000..c5a01c96 --- /dev/null +++ b/docs/Get-OBSDisplacementMapAdvancedShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedShader +------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |14 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +``` From 10a472b85b04457df2e8dc79bc418dc078609709 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 087/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSDisplacementMapAdvancedShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json diff --git a/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json new file mode 100644 index 00000000..30e965e9 --- /dev/null +++ b/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From cd9531d4d14107b8a4ed80a396a83023ffbc318e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 088/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSDisplacementMapInvertShader.md | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Get-OBSDisplacementMapInvertShader.md diff --git a/docs/Get-OBSDisplacementMapInvertShader.md b/docs/Get-OBSDisplacementMapInvertShader.md new file mode 100644 index 00000000..15bd7d81 --- /dev/null +++ b/docs/Get-OBSDisplacementMapInvertShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapInvertShader +---------------------------------- + +### Synopsis + +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |3 |false |background_layer| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From 177597bb2b5d08458312e702e18b8152ea015bb7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 089/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Get-OBSDisplacementMapInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSDisplacementMapInvertShader.json diff --git a/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json new file mode 100644 index 00000000..13248875 --- /dev/null +++ b/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 3892173ff7668295858f0bb5de9b2e803458fefd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:17 +0000 Subject: [PATCH 090/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSDisplacementMapShader.md | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Get-OBSDisplacementMapShader.md diff --git a/docs/Get-OBSDisplacementMapShader.md b/docs/Get-OBSDisplacementMapShader.md new file mode 100644 index 00000000..311c6f56 --- /dev/null +++ b/docs/Get-OBSDisplacementMapShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapShader +---------------------------- + +### Synopsis + +Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |3 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From d13a886151d05acb4b5581c05bca16c7c8d336b3 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:18 +0000 Subject: [PATCH 091/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Get-OBSDisplacementMapShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSDisplacementMapShader.json diff --git a/docs/_data/Help/Get-OBSDisplacementMapShader.json b/docs/_data/Help/Get-OBSDisplacementMapShader.json new file mode 100644 index 00000000..82fe9189 --- /dev/null +++ b/docs/_data/Help/Get-OBSDisplacementMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From c2f8b826ea6a4cb541e056d94cfa2836e38c04d8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:22 +0000 Subject: [PATCH 092/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSGlitchPeriodicShader.md | 102 ++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Get-OBSGlitchPeriodicShader.md diff --git a/docs/Get-OBSGlitchPeriodicShader.md b/docs/Get-OBSGlitchPeriodicShader.md new file mode 100644 index 00000000..330bc10b --- /dev/null +++ b/docs/Get-OBSGlitchPeriodicShader.md @@ -0,0 +1,102 @@ +Get-OBSGlitchPeriodicShader +--------------------------- + +### Synopsis + +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AMPL** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **DURA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **SCRA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +``` From 6ec34c9e44de5e73f9a3142a1b10a5dd59b7b2da Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:22 +0000 Subject: [PATCH 093/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Get-OBSGlitchPeriodicShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSGlitchPeriodicShader.json diff --git a/docs/_data/Help/Get-OBSGlitchPeriodicShader.json b/docs/_data/Help/Get-OBSGlitchPeriodicShader.json new file mode 100644 index 00000000..08609385 --- /dev/null +++ b/docs/_data/Help/Get-OBSGlitchPeriodicShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 34f1b3dc7cf100061e5344fc5cf843600c324253 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:23 +0000 Subject: [PATCH 094/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSHardBlinkShader.md | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Get-OBSHardBlinkShader.md b/docs/Get-OBSHardBlinkShader.md index 9be98bd9..63748d2e 100644 --- a/docs/Get-OBSHardBlinkShader.md +++ b/docs/Get-OBSHardBlinkShader.md @@ -2,7 +2,6 @@ Get-OBSHardBlinkShader ---------------------- ### Synopsis - Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] < ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Timeoff** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **Timeon** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From b43691030761914708ba96d264ab7795febea316 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:23 +0000 Subject: [PATCH 095/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSHardBlinkShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSHardBlinkShader.json b/docs/_data/Help/Get-OBSHardBlinkShader.json index 43665759..cf41ebe3 100644 --- a/docs/_data/Help/Get-OBSHardBlinkShader.json +++ b/docs/_data/Help/Get-OBSHardBlinkShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 4644a4222d6fe9766ef431aa258d4421410a7716 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:25 +0000 Subject: [PATCH 096/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSMotionBlurShader.md | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Get-OBSMotionBlurShader.md b/docs/Get-OBSMotionBlurShader.md index 6788858e..0291ea14 100644 --- a/docs/Get-OBSMotionBlurShader.md +++ b/docs/Get-OBSMotionBlurShader.md @@ -2,7 +2,6 @@ Get-OBSMotionBlurShader ----------------------- ### Synopsis - Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-So ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PreviousOutput** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------| -|`[string]`|false |0 |false |previous_output| +|`[String]`|false |named |False |previous_output| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From e3b173579e8e09ce0ecc87d038711922ec52e1db Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:26 +0000 Subject: [PATCH 097/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSMotionBlurShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSMotionBlurShader.json b/docs/_data/Help/Get-OBSMotionBlurShader.json index eb301da6..183df040 100644 --- a/docs/_data/Help/Get-OBSMotionBlurShader.json +++ b/docs/_data/Help/Get-OBSMotionBlurShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From d70deb001582d4e387355c2e4c5ec622763f9641 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:26 +0000 Subject: [PATCH 098/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSNoiseShader.md | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Get-OBSNoiseShader.md diff --git a/docs/Get-OBSNoiseShader.md b/docs/Get-OBSNoiseShader.md new file mode 100644 index 00000000..6fa983dc --- /dev/null +++ b/docs/Get-OBSNoiseShader.md @@ -0,0 +1,108 @@ +Get-OBSNoiseShader +------------------ + +### Synopsis + +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Monochromatic** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoiseLevel** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseRand** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------| +|`[switch]`|false |Named |false |use_rand| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +``` From 3272a1a7ff730c2b71bf934c2d462e033cdf1187 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:26 +0000 Subject: [PATCH 099/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSNoiseShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSNoiseShader.json diff --git a/docs/_data/Help/Get-OBSNoiseShader.json b/docs/_data/Help/Get-OBSNoiseShader.json new file mode 100644 index 00000000..db2627eb --- /dev/null +++ b/docs/_data/Help/Get-OBSNoiseShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 575c83a033b8f65e6385d776b903fe00ef21df9b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:26 +0000 Subject: [PATCH 100/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSNormalMapShader.md | 114 +++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Get-OBSNormalMapShader.md diff --git a/docs/Get-OBSNormalMapShader.md b/docs/Get-OBSNormalMapShader.md new file mode 100644 index 00000000..ef22fc4d --- /dev/null +++ b/docs/Get-OBSNormalMapShader.md @@ -0,0 +1,114 @@ +Get-OBSNormalMapShader +---------------------- + +### Synopsis + +Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertG** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertH** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertR** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OffsetHeight** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **Type** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNormalMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From 7fafb745355f8791c2c7e887466179dec05f93e2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:26 +0000 Subject: [PATCH 101/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSNormalMapShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSNormalMapShader.json diff --git a/docs/_data/Help/Get-OBSNormalMapShader.json b/docs/_data/Help/Get-OBSNormalMapShader.json new file mode 100644 index 00000000..31f25d5e --- /dev/null +++ b/docs/_data/Help/Get-OBSNormalMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From e1dde16e556e79e47e977721b818d96395de5546 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:27 +0000 Subject: [PATCH 102/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSPerspectiveShader.md | 43 +++++++++++++------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Get-OBSPerspectiveShader.md b/docs/Get-OBSPerspectiveShader.md index b39795f2..3288693b 100644 --- a/docs/Get-OBSPerspectiveShader.md +++ b/docs/Get-OBSPerspectiveShader.md @@ -2,7 +2,6 @@ Get-OBSPerspectiveShader ------------------------ ### Synopsis - Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,79 +15,79 @@ Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [-ShowBorder ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From b7e136220976e234126b2572137069cbec68979b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:27 +0000 Subject: [PATCH 103/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSPerspectiveShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSPerspectiveShader.json b/docs/_data/Help/Get-OBSPerspectiveShader.json index 43953a39..c0615f02 100644 --- a/docs/_data/Help/Get-OBSPerspectiveShader.json +++ b/docs/_data/Help/Get-OBSPerspectiveShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 4223ab210d722e618fb8d561caba8ff175925ef7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:28 +0000 Subject: [PATCH 104/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSQuadrilateralCropShader.md | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/Get-OBSQuadrilateralCropShader.md diff --git a/docs/Get-OBSQuadrilateralCropShader.md b/docs/Get-OBSQuadrilateralCropShader.md new file mode 100644 index 00000000..611c85ce --- /dev/null +++ b/docs/Get-OBSQuadrilateralCropShader.md @@ -0,0 +1,126 @@ +Get-OBSQuadrilateralCropShader +------------------------------ + +### Synopsis + +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BottomLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |4 |false |Bottom_Left_X| + +#### **BottomLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |5 |false |Bottom_Left_Y| + +#### **BottomRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |6 |false |Bottom_Right_X| + +#### **BottomRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |7 |false |Bottom_Right_Y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |9 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |10 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| + +#### **TopLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |Top_Left_X| + +#### **TopLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |1 |false |Top_Left_Y| + +#### **TopRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |2 |false |Top_Right_X| + +#### **TopRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |3 |false |Top_Right_Y| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From 2ade993a863d0caba21aa495cd4f4f07c745c847 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:28 +0000 Subject: [PATCH 105/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Get-OBSQuadrilateralCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSQuadrilateralCropShader.json diff --git a/docs/_data/Help/Get-OBSQuadrilateralCropShader.json b/docs/_data/Help/Get-OBSQuadrilateralCropShader.json new file mode 100644 index 00000000..e0609f0e --- /dev/null +++ b/docs/_data/Help/Get-OBSQuadrilateralCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 50e959d99f54b73c1be3c5e5db917decf279e9a7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:29 +0000 Subject: [PATCH 106/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSRepeatGridCenterCropShader.md | 45 ++++++++++------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/Get-OBSRepeatGridCenterCropShader.md b/docs/Get-OBSRepeatGridCenterCropShader.md index bd68baeb..dd7f8e8a 100644 --- a/docs/Get-OBSRepeatGridCenterCropShader.md +++ b/docs/Get-OBSRepeatGridCenterCropShader.md @@ -2,7 +2,6 @@ Get-OBSRepeatGridCenterCropShader --------------------------------- ### Synopsis - Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **Columns** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |6 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Image** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[string]`|false |1 |false | +|`[String]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Rows** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |4 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |7 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ViewProj** -|Type |Required|Position|PipelineInput| -|-------------|--------|--------|-------------| -|`[float[][]]`|false |0 |false | +|Type |Required|Position|PipelineInput| +|---------------------|--------|--------|-------------| +|`[System.Single[][]]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From e38b2fd4ee056e7e5af5ad3d6c1ae821c12c5eed Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:29 +0000 Subject: [PATCH 107/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json index c4ac45ff..25e86ac4 100644 --- a/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json +++ b/docs/_data/Help/Get-OBSRepeatGridCenterCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 3ed413cc9c5dac6a6cb5e85fe5dabc28d39f4a36 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:35 +0000 Subject: [PATCH 108/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSWalkingDeadPixelFixerShader.md | 51 ++++++++++------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Get-OBSWalkingDeadPixelFixerShader.md b/docs/Get-OBSWalkingDeadPixelFixerShader.md index fb0a4df0..dab40694 100644 --- a/docs/Get-OBSWalkingDeadPixelFixerShader.md +++ b/docs/Get-OBSWalkingDeadPixelFixerShader.md @@ -2,7 +2,6 @@ Get-OBSWalkingDeadPixelFixerShader ---------------------------------- ### Synopsis - Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,103 +15,103 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[ |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ContrastThreshold** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------------| -|`[float]`|false |4 |false |Contrast_Threshold| +|`[Float]`|false |named |False |Contrast_Threshold| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |8 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaxClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |6 |false |Max_Cluster_Size| +|`[Int]`|false |named |False |Max_Cluster_Size| #### **MinClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |5 |false |Min_Cluster_Size| +|`[Int]`|false |named |False |Min_Cluster_Size| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ScanHeight** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-----------| -|`[int]`|false |1 |false |Scan_Height| +|`[Int]`|false |named |False |Scan_Height| #### **ScanOffsetX** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |2 |false |Scan_Offset_X| +|`[Int]`|false |named |False |Scan_Offset_X| #### **ScanOffsetY** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |3 |false |Scan_Offset_Y| +|`[Int]`|false |named |False |Scan_Offset_Y| #### **ScanWidth** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------| -|`[int]`|false |0 |false |Scan_Width| +|`[Int]`|false |named |False |Scan_Width| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |9 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **ShowBorder** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |Show_Border| +|`[Switch]`|false |named |False |Show_Border| #### **ShowGreen** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[switch]`|false |Named |false |Show_Green| +|`[Switch]`|false |named |False |Show_Green| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -128,11 +127,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [-ShowBorder ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [-ShowGreen ] [-Bypass ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 950541dfc6e740b5efb82fe74c59e411a8ac94c5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:35 +0000 Subject: [PATCH 109/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json index 858f3e99..64dbd980 100644 --- a/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json +++ b/docs/_data/Help/Get-OBSWalkingDeadPixelFixerShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From cdcdb18b1ef980db4a9aae1334c5c2d9a9a54934 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:36 +0000 Subject: [PATCH 110/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Get-OBSZoomBlurTransitionShader.md | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Get-OBSZoomBlurTransitionShader.md diff --git a/docs/Get-OBSZoomBlurTransitionShader.md b/docs/Get-OBSZoomBlurTransitionShader.md new file mode 100644 index 00000000..5859f433 --- /dev/null +++ b/docs/Get-OBSZoomBlurTransitionShader.md @@ -0,0 +1,108 @@ +Get-OBSZoomBlurTransitionShader +------------------------------- + +### Synopsis + +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **ConvertLinear** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[switch]`|false |Named |false |convert_linear| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ImageA** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |0 |false |image_a| + +#### **ImageB** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |1 |false |image_b| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **TransitionTime** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |2 |false |transition_time| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +``` From d6ac73ded3db9cb75cb9f20750b3e8711ba5c733 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:36 +0000 Subject: [PATCH 111/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Get-OBSZoomBlurTransitionShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Get-OBSZoomBlurTransitionShader.json diff --git a/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json new file mode 100644 index 00000000..c14ca126 --- /dev/null +++ b/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From a2fa7271fa4ed1798e5112ea07e0341416aee2b3 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:46 +0000 Subject: [PATCH 112/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBS3dPanelShader.md | 57 ++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/docs/Add-OBS3dPanelShader.md b/docs/Add-OBS3dPanelShader.md index 5079fd77..c72918bf 100644 --- a/docs/Add-OBS3dPanelShader.md +++ b/docs/Add-OBS3dPanelShader.md @@ -2,7 +2,6 @@ Get-OBS3dPanelShader -------------------- ### Synopsis - Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,121 +15,121 @@ Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [-WiggleRot ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From fd1ec4d31f571f13e196ff1beca59340efd80596 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:46 +0000 Subject: [PATCH 113/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBS3dPanelShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBS3dPanelShader.json b/docs/_data/Help/Add-OBS3dPanelShader.json index fe53f972..3e26a529 100644 --- a/docs/_data/Help/Add-OBS3dPanelShader.json +++ b/docs/_data/Help/Add-OBS3dPanelShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 8a9ec92965261c5023266a0e8000012e82633544 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:46 +0000 Subject: [PATCH 114/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSAudioShader.md | 96 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/Add-OBSAudioShader.md diff --git a/docs/Add-OBSAudioShader.md b/docs/Add-OBSAudioShader.md new file mode 100644 index 00000000..627857c6 --- /dev/null +++ b/docs/Add-OBSAudioShader.md @@ -0,0 +1,96 @@ +Get-OBSAudioShader +------------------ + +### Synopsis + +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AudioMagnitude** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |1 |false |audio_magnitude| + +#### **AudioPeak** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |audio_peak| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Intensity** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +``` From 6f806ff625f52cb2b506038c4b441eb7a8267773 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:47 +0000 Subject: [PATCH 115/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSAudioShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSAudioShader.json diff --git a/docs/_data/Help/Add-OBSAudioShader.json b/docs/_data/Help/Add-OBSAudioShader.json new file mode 100644 index 00000000..3e439275 --- /dev/null +++ b/docs/_data/Help/Add-OBSAudioShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 9473550dc41afbf6eb94f9025c5c39dc4aa48b5a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:49 +0000 Subject: [PATCH 116/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSCubeRotatingShader.md | 43 +++++++++++++------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Add-OBSCubeRotatingShader.md b/docs/Add-OBSCubeRotatingShader.md index 5c393585..ca9ab33e 100644 --- a/docs/Add-OBSCubeRotatingShader.md +++ b/docs/Add-OBSCubeRotatingShader.md @@ -2,7 +2,6 @@ Get-OBSCubeRotatingShader ------------------------- ### Synopsis - Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 510ac6fe7ee980a43ef3624ca675923880f528f6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:49 +0000 Subject: [PATCH 117/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSCubeRotatingShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSCubeRotatingShader.json b/docs/_data/Help/Add-OBSCubeRotatingShader.json index 8d837b62..39b20764 100644 --- a/docs/_data/Help/Add-OBSCubeRotatingShader.json +++ b/docs/_data/Help/Add-OBSCubeRotatingShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From e569f391dc1bd57947f24736e46251ecb5daec71 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:49 +0000 Subject: [PATCH 118/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...-OBSDisplacementMapAdvancedInvertShader.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Add-OBSDisplacementMapAdvancedInvertShader.md diff --git a/docs/Add-OBSDisplacementMapAdvancedInvertShader.md b/docs/Add-OBSDisplacementMapAdvancedInvertShader.md new file mode 100644 index 00000000..94e327ca --- /dev/null +++ b/docs/Add-OBSDisplacementMapAdvancedInvertShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedInvertShader +------------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |14 |false |background_layer| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From bb3502e137bf16a5823ed630389818c8f2219d8b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:49 +0000 Subject: [PATCH 119/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...BSDisplacementMapAdvancedInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json diff --git a/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json new file mode 100644 index 00000000..02d480c7 --- /dev/null +++ b/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 4995ab932654c646c7029c776f4e09191e75d9ae Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:49 +0000 Subject: [PATCH 120/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSDisplacementMapAdvancedShader.md | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Add-OBSDisplacementMapAdvancedShader.md diff --git a/docs/Add-OBSDisplacementMapAdvancedShader.md b/docs/Add-OBSDisplacementMapAdvancedShader.md new file mode 100644 index 00000000..c5a01c96 --- /dev/null +++ b/docs/Add-OBSDisplacementMapAdvancedShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedShader +------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |14 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +``` From f2f9a1af51803bf9a240099792191198cc0da559 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:50 +0000 Subject: [PATCH 121/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Add-OBSDisplacementMapAdvancedShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json diff --git a/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json new file mode 100644 index 00000000..30e965e9 --- /dev/null +++ b/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 571db351d41e3ce4ac9dfc61fe77fcc63fe2e88a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:50 +0000 Subject: [PATCH 122/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSDisplacementMapInvertShader.md | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Add-OBSDisplacementMapInvertShader.md diff --git a/docs/Add-OBSDisplacementMapInvertShader.md b/docs/Add-OBSDisplacementMapInvertShader.md new file mode 100644 index 00000000..15bd7d81 --- /dev/null +++ b/docs/Add-OBSDisplacementMapInvertShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapInvertShader +---------------------------------- + +### Synopsis + +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |3 |false |background_layer| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From 35ee724bea1632a6d4cc20356c0df292ccb92907 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:50 +0000 Subject: [PATCH 123/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Add-OBSDisplacementMapInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSDisplacementMapInvertShader.json diff --git a/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json new file mode 100644 index 00000000..13248875 --- /dev/null +++ b/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From d51b010bcc35882996d966ab4dfbdffde9342728 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:50 +0000 Subject: [PATCH 124/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSDisplacementMapShader.md | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Add-OBSDisplacementMapShader.md diff --git a/docs/Add-OBSDisplacementMapShader.md b/docs/Add-OBSDisplacementMapShader.md new file mode 100644 index 00000000..311c6f56 --- /dev/null +++ b/docs/Add-OBSDisplacementMapShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapShader +---------------------------- + +### Synopsis + +Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |3 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From 3968eb0bf9d80461236e2c21980f6c9663d5a816 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:50 +0000 Subject: [PATCH 125/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Add-OBSDisplacementMapShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSDisplacementMapShader.json diff --git a/docs/_data/Help/Add-OBSDisplacementMapShader.json b/docs/_data/Help/Add-OBSDisplacementMapShader.json new file mode 100644 index 00000000..82fe9189 --- /dev/null +++ b/docs/_data/Help/Add-OBSDisplacementMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From c18fb2155309c321120a596e3843134161fd4926 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:53 +0000 Subject: [PATCH 126/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSGlitchPeriodicShader.md | 102 ++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Add-OBSGlitchPeriodicShader.md diff --git a/docs/Add-OBSGlitchPeriodicShader.md b/docs/Add-OBSGlitchPeriodicShader.md new file mode 100644 index 00000000..330bc10b --- /dev/null +++ b/docs/Add-OBSGlitchPeriodicShader.md @@ -0,0 +1,102 @@ +Get-OBSGlitchPeriodicShader +--------------------------- + +### Synopsis + +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AMPL** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **DURA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **SCRA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +``` From 33e9f8c34e7038119b507f11652cfaa57010d168 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:53 +0000 Subject: [PATCH 127/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Add-OBSGlitchPeriodicShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSGlitchPeriodicShader.json diff --git a/docs/_data/Help/Add-OBSGlitchPeriodicShader.json b/docs/_data/Help/Add-OBSGlitchPeriodicShader.json new file mode 100644 index 00000000..08609385 --- /dev/null +++ b/docs/_data/Help/Add-OBSGlitchPeriodicShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From d99bd93a07b19115c411532c21898863c4f093b0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:54 +0000 Subject: [PATCH 128/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSHardBlinkShader.md | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Add-OBSHardBlinkShader.md b/docs/Add-OBSHardBlinkShader.md index 9be98bd9..63748d2e 100644 --- a/docs/Add-OBSHardBlinkShader.md +++ b/docs/Add-OBSHardBlinkShader.md @@ -2,7 +2,6 @@ Get-OBSHardBlinkShader ---------------------- ### Synopsis - Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] < ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Timeoff** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **Timeon** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From cebb28e37e326205fcfdf2ce05c6f91a96b7e694 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:54 +0000 Subject: [PATCH 129/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSHardBlinkShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSHardBlinkShader.json b/docs/_data/Help/Add-OBSHardBlinkShader.json index 43665759..cf41ebe3 100644 --- a/docs/_data/Help/Add-OBSHardBlinkShader.json +++ b/docs/_data/Help/Add-OBSHardBlinkShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 035c17877377801b6dc490ec6e1ceb2da1d33358 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 130/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSMotionBlurShader.md | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Add-OBSMotionBlurShader.md b/docs/Add-OBSMotionBlurShader.md index 6788858e..0291ea14 100644 --- a/docs/Add-OBSMotionBlurShader.md +++ b/docs/Add-OBSMotionBlurShader.md @@ -2,7 +2,6 @@ Get-OBSMotionBlurShader ----------------------- ### Synopsis - Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-So ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PreviousOutput** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------| -|`[string]`|false |0 |false |previous_output| +|`[String]`|false |named |False |previous_output| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 4dfbcdc50063d89499585ce622aea3932187c24a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 131/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSMotionBlurShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSMotionBlurShader.json b/docs/_data/Help/Add-OBSMotionBlurShader.json index eb301da6..183df040 100644 --- a/docs/_data/Help/Add-OBSMotionBlurShader.json +++ b/docs/_data/Help/Add-OBSMotionBlurShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 44f4430ef4cfa26419a1239d5917d1bc72a77bdb Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 132/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSNoiseShader.md | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Add-OBSNoiseShader.md diff --git a/docs/Add-OBSNoiseShader.md b/docs/Add-OBSNoiseShader.md new file mode 100644 index 00000000..6fa983dc --- /dev/null +++ b/docs/Add-OBSNoiseShader.md @@ -0,0 +1,108 @@ +Get-OBSNoiseShader +------------------ + +### Synopsis + +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Monochromatic** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoiseLevel** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseRand** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------| +|`[switch]`|false |Named |false |use_rand| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +``` From 7574f1b6951b258a68a72b5ddae37307053f2dd4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 133/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSNoiseShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSNoiseShader.json diff --git a/docs/_data/Help/Add-OBSNoiseShader.json b/docs/_data/Help/Add-OBSNoiseShader.json new file mode 100644 index 00000000..db2627eb --- /dev/null +++ b/docs/_data/Help/Add-OBSNoiseShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From d1214c091d25e1cf7674109692cd561dfb8c77f9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 134/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSNormalMapShader.md | 114 +++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Add-OBSNormalMapShader.md diff --git a/docs/Add-OBSNormalMapShader.md b/docs/Add-OBSNormalMapShader.md new file mode 100644 index 00000000..ef22fc4d --- /dev/null +++ b/docs/Add-OBSNormalMapShader.md @@ -0,0 +1,114 @@ +Get-OBSNormalMapShader +---------------------- + +### Synopsis + +Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertG** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertH** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertR** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OffsetHeight** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **Type** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNormalMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From 71a38782d2d6cba2ea39d279eb166899c19aa2b0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:55 +0000 Subject: [PATCH 135/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSNormalMapShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSNormalMapShader.json diff --git a/docs/_data/Help/Add-OBSNormalMapShader.json b/docs/_data/Help/Add-OBSNormalMapShader.json new file mode 100644 index 00000000..31f25d5e --- /dev/null +++ b/docs/_data/Help/Add-OBSNormalMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From a6122fc8eaa94c2d35481fad089af7757053e801 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:56 +0000 Subject: [PATCH 136/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSPerspectiveShader.md | 43 +++++++++++++------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Add-OBSPerspectiveShader.md b/docs/Add-OBSPerspectiveShader.md index b39795f2..3288693b 100644 --- a/docs/Add-OBSPerspectiveShader.md +++ b/docs/Add-OBSPerspectiveShader.md @@ -2,7 +2,6 @@ Get-OBSPerspectiveShader ------------------------ ### Synopsis - Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,79 +15,79 @@ Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [-ShowBorder ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 76c003d2abd2ef6fcca27aa5a791bfbf062cc2b5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:56 +0000 Subject: [PATCH 137/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSPerspectiveShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSPerspectiveShader.json b/docs/_data/Help/Add-OBSPerspectiveShader.json index 43953a39..c0615f02 100644 --- a/docs/_data/Help/Add-OBSPerspectiveShader.json +++ b/docs/_data/Help/Add-OBSPerspectiveShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 4f51c53c0833b55561d560b75b0de5803a5c743d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:56 +0000 Subject: [PATCH 138/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSQuadrilateralCropShader.md | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/Add-OBSQuadrilateralCropShader.md diff --git a/docs/Add-OBSQuadrilateralCropShader.md b/docs/Add-OBSQuadrilateralCropShader.md new file mode 100644 index 00000000..611c85ce --- /dev/null +++ b/docs/Add-OBSQuadrilateralCropShader.md @@ -0,0 +1,126 @@ +Get-OBSQuadrilateralCropShader +------------------------------ + +### Synopsis + +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BottomLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |4 |false |Bottom_Left_X| + +#### **BottomLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |5 |false |Bottom_Left_Y| + +#### **BottomRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |6 |false |Bottom_Right_X| + +#### **BottomRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |7 |false |Bottom_Right_Y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |9 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |10 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| + +#### **TopLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |Top_Left_X| + +#### **TopLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |1 |false |Top_Left_Y| + +#### **TopRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |2 |false |Top_Right_X| + +#### **TopRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |3 |false |Top_Right_Y| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From 0f9a929529b324e9bc3c27c40210ec85bba598e6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:57 +0000 Subject: [PATCH 139/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Add-OBSQuadrilateralCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSQuadrilateralCropShader.json diff --git a/docs/_data/Help/Add-OBSQuadrilateralCropShader.json b/docs/_data/Help/Add-OBSQuadrilateralCropShader.json new file mode 100644 index 00000000..e0609f0e --- /dev/null +++ b/docs/_data/Help/Add-OBSQuadrilateralCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From b097f4bbbf32738809f86bdf991ef305947f7021 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:57 +0000 Subject: [PATCH 140/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSRepeatGridCenterCropShader.md | 45 ++++++++++------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/Add-OBSRepeatGridCenterCropShader.md b/docs/Add-OBSRepeatGridCenterCropShader.md index bd68baeb..dd7f8e8a 100644 --- a/docs/Add-OBSRepeatGridCenterCropShader.md +++ b/docs/Add-OBSRepeatGridCenterCropShader.md @@ -2,7 +2,6 @@ Get-OBSRepeatGridCenterCropShader --------------------------------- ### Synopsis - Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **Columns** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |6 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Image** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[string]`|false |1 |false | +|`[String]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Rows** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |4 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |7 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ViewProj** -|Type |Required|Position|PipelineInput| -|-------------|--------|--------|-------------| -|`[float[][]]`|false |0 |false | +|Type |Required|Position|PipelineInput| +|---------------------|--------|--------|-------------| +|`[System.Single[][]]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From c24e5d13d602e8d2dbef9b09324cc5a90ce5a664 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:54:57 +0000 Subject: [PATCH 141/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json index c4ac45ff..25e86ac4 100644 --- a/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json +++ b/docs/_data/Help/Add-OBSRepeatGridCenterCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 7f8deb58ce13c4704a60a770ddcbe0c501f00e49 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:01 +0000 Subject: [PATCH 142/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSWalkingDeadPixelFixerShader.md | 51 ++++++++++------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Add-OBSWalkingDeadPixelFixerShader.md b/docs/Add-OBSWalkingDeadPixelFixerShader.md index fb0a4df0..dab40694 100644 --- a/docs/Add-OBSWalkingDeadPixelFixerShader.md +++ b/docs/Add-OBSWalkingDeadPixelFixerShader.md @@ -2,7 +2,6 @@ Get-OBSWalkingDeadPixelFixerShader ---------------------------------- ### Synopsis - Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,103 +15,103 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[ |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ContrastThreshold** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------------| -|`[float]`|false |4 |false |Contrast_Threshold| +|`[Float]`|false |named |False |Contrast_Threshold| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |8 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaxClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |6 |false |Max_Cluster_Size| +|`[Int]`|false |named |False |Max_Cluster_Size| #### **MinClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |5 |false |Min_Cluster_Size| +|`[Int]`|false |named |False |Min_Cluster_Size| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ScanHeight** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-----------| -|`[int]`|false |1 |false |Scan_Height| +|`[Int]`|false |named |False |Scan_Height| #### **ScanOffsetX** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |2 |false |Scan_Offset_X| +|`[Int]`|false |named |False |Scan_Offset_X| #### **ScanOffsetY** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |3 |false |Scan_Offset_Y| +|`[Int]`|false |named |False |Scan_Offset_Y| #### **ScanWidth** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------| -|`[int]`|false |0 |false |Scan_Width| +|`[Int]`|false |named |False |Scan_Width| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |9 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **ShowBorder** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |Show_Border| +|`[Switch]`|false |named |False |Show_Border| #### **ShowGreen** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[switch]`|false |Named |false |Show_Green| +|`[Switch]`|false |named |False |Show_Green| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -128,11 +127,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [-ShowBorder ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [-ShowGreen ] [-Bypass ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 516d475d5dc44146ff74e1f59fce2e6cc5e7f7e4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:01 +0000 Subject: [PATCH 143/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json index 858f3e99..64dbd980 100644 --- a/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json +++ b/docs/_data/Help/Add-OBSWalkingDeadPixelFixerShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 616627b46e799a660eb97545465c84166aef4174 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:02 +0000 Subject: [PATCH 144/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Add-OBSZoomBlurTransitionShader.md | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Add-OBSZoomBlurTransitionShader.md diff --git a/docs/Add-OBSZoomBlurTransitionShader.md b/docs/Add-OBSZoomBlurTransitionShader.md new file mode 100644 index 00000000..5859f433 --- /dev/null +++ b/docs/Add-OBSZoomBlurTransitionShader.md @@ -0,0 +1,108 @@ +Get-OBSZoomBlurTransitionShader +------------------------------- + +### Synopsis + +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **ConvertLinear** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[switch]`|false |Named |false |convert_linear| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ImageA** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |0 |false |image_a| + +#### **ImageB** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |1 |false |image_b| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **TransitionTime** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |2 |false |transition_time| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +``` From ae50236fc72a8c37b846de67d663c54e9170c78d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:02 +0000 Subject: [PATCH 145/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Add-OBSZoomBlurTransitionShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Add-OBSZoomBlurTransitionShader.json diff --git a/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json new file mode 100644 index 00000000..c14ca126 --- /dev/null +++ b/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 919e4af2680d2deb673fa4382f0a853646ec5ec1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:11 +0000 Subject: [PATCH 146/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBS3dPanelShader.md | 57 ++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/docs/Set-OBS3dPanelShader.md b/docs/Set-OBS3dPanelShader.md index 5079fd77..c72918bf 100644 --- a/docs/Set-OBS3dPanelShader.md +++ b/docs/Set-OBS3dPanelShader.md @@ -2,7 +2,6 @@ Get-OBS3dPanelShader -------------------- ### Synopsis - Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,121 +15,121 @@ Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [-WiggleRot ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 87904cdcba14e222a9e713fb461cec9d3ebb8168 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:11 +0000 Subject: [PATCH 147/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBS3dPanelShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBS3dPanelShader.json b/docs/_data/Help/Set-OBS3dPanelShader.json index fe53f972..3e26a529 100644 --- a/docs/_data/Help/Set-OBS3dPanelShader.json +++ b/docs/_data/Help/Set-OBS3dPanelShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBS3dPanelShader [[-Credits] ] [[-Scale] ] [[-TiltXDeg] ] [[-TiltYDeg] ] [[-TiltZDeg] ] [[-PosX] ] [[-PosY] ] [[-Thickness] ] [[-RadiusFb] ] [[-Brightness] ] [[-LightPosition] ] [[-Wiggle] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 8d8c9a6ea25fc36c08abcdccf035fcd06096935d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:12 +0000 Subject: [PATCH 148/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSAudioShader.md | 96 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/Set-OBSAudioShader.md diff --git a/docs/Set-OBSAudioShader.md b/docs/Set-OBSAudioShader.md new file mode 100644 index 00000000..627857c6 --- /dev/null +++ b/docs/Set-OBSAudioShader.md @@ -0,0 +1,96 @@ +Get-OBSAudioShader +------------------ + +### Synopsis + +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AudioMagnitude** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |1 |false |audio_magnitude| + +#### **AudioPeak** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |audio_peak| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Intensity** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +``` From d48d0ee2ac20efd6d31ac91dbb75f217a98632b1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:12 +0000 Subject: [PATCH 149/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSAudioShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSAudioShader.json diff --git a/docs/_data/Help/Set-OBSAudioShader.json b/docs/_data/Help/Set-OBSAudioShader.json new file mode 100644 index 00000000..3e439275 --- /dev/null +++ b/docs/_data/Help/Set-OBSAudioShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 3b797282bb453e0c3de025ebc4cf3a620d702e05 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:14 +0000 Subject: [PATCH 150/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSCubeRotatingShader.md | 43 +++++++++++++------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Set-OBSCubeRotatingShader.md b/docs/Set-OBSCubeRotatingShader.md index 5c393585..ca9ab33e 100644 --- a/docs/Set-OBSCubeRotatingShader.md +++ b/docs/Set-OBSCubeRotatingShader.md @@ -2,7 +2,6 @@ Get-OBSCubeRotatingShader ------------------------- ### Synopsis - Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 25626bbd50f92d94922b875936fbc655cd1b0483 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:14 +0000 Subject: [PATCH 151/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSCubeRotatingShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSCubeRotatingShader.json b/docs/_data/Help/Set-OBSCubeRotatingShader.json index 8d837b62..39b20764 100644 --- a/docs/_data/Help/Set-OBSCubeRotatingShader.json +++ b/docs/_data/Help/Set-OBSCubeRotatingShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSCubeRotatingShader [[-Images] ] [[-Speed] ] [[-Shadow] ] [[-OtherImage1] ] [[-OtherImage2] ] [[-OtherImage3] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 4c748b8f21253f9e86f1c075cd9a6ad39a385897 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 152/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...-OBSDisplacementMapAdvancedInvertShader.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Set-OBSDisplacementMapAdvancedInvertShader.md diff --git a/docs/Set-OBSDisplacementMapAdvancedInvertShader.md b/docs/Set-OBSDisplacementMapAdvancedInvertShader.md new file mode 100644 index 00000000..94e327ca --- /dev/null +++ b/docs/Set-OBSDisplacementMapAdvancedInvertShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedInvertShader +------------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |14 |false |background_layer| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From fa2ea9bfb4063fe3e95da17300dc31091d4c0a3c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 153/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- ...BSDisplacementMapAdvancedInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json diff --git a/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json new file mode 100644 index 00000000..02d480c7 --- /dev/null +++ b/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 7d56418520735e0d9e0ce509d95b9f86bbbc57c4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 154/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSDisplacementMapAdvancedShader.md | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/Set-OBSDisplacementMapAdvancedShader.md diff --git a/docs/Set-OBSDisplacementMapAdvancedShader.md b/docs/Set-OBSDisplacementMapAdvancedShader.md new file mode 100644 index 00000000..c5a01c96 --- /dev/null +++ b/docs/Set-OBSDisplacementMapAdvancedShader.md @@ -0,0 +1,198 @@ +Get-OBSDisplacementMapAdvancedShader +------------------------------------ + +### Synopsis + +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AlphaAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------------| +|`[switch]`|false |Named |false |alpha_affects_strength| + +#### **ApplyAlpha** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------| +|`[switch]`|false |Named |false |apply_alpha| + +#### **BlueAffectsBlur** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[switch]`|false |Named |false |blue_affects_blur| + +#### **BlueAffectsColorize** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_colorize| + +#### **BlueAffectsStrength** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------------------| +|`[switch]`|false |Named |false |blue_affects_strength| + +#### **BlurAngle** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |8 |false |blur_angle| + +#### **BlurDirections** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |7 |false |blur_directions| + +#### **BlurInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|---------| +|`[string]`|false |4 |false |blur_info| + +#### **BlurQuality** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|------------| +|`[float]`|false |6 |false |blur_quality| + +#### **BlurSize** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------| +|`[float]`|false |5 |false |blur_size| + +#### **ChromaticAberration** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------------| +|`[float]`|false |10 |false |chromatic_aberration| + +#### **ChromaticAberrationInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------------------| +|`[string]`|false |9 |false |chromatic_aberration_info| + +#### **ColorizeColor** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[string]`|false |12 |false |colorize_color| + +#### **ColorizeInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |11 |false |colorize_info| + +#### **DisplacementCurve** + +|Type |Required|Position|PipelineInput|Aliases | +|-------|--------|--------|-------------|------------------| +|`[int]`|false |3 |false |displacement_curve| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |16 |true (ByPropertyName)| + +#### **FlagsInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |13 |false |flags_info| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |14 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |17 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +``` From 479af53ea347e594433cd883f9cfd34e2bbc1853 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 155/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Set-OBSDisplacementMapAdvancedShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json diff --git a/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json new file mode 100644 index 00000000..30e965e9 --- /dev/null +++ b/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From c4a5bff9f1667266edb1567c4e38eff0e96ba8a5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 156/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSDisplacementMapInvertShader.md | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Set-OBSDisplacementMapInvertShader.md diff --git a/docs/Set-OBSDisplacementMapInvertShader.md b/docs/Set-OBSDisplacementMapInvertShader.md new file mode 100644 index 00000000..15bd7d81 --- /dev/null +++ b/docs/Set-OBSDisplacementMapInvertShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapInvertShader +---------------------------------- + +### Synopsis + +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BackgroundLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------------| +|`[string]`|false |3 |false |background_layer| + +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +``` From c2b7331ddcd562ef838bb63288194fe02114e1db Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 157/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Set-OBSDisplacementMapInvertShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSDisplacementMapInvertShader.json diff --git a/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json new file mode 100644 index 00000000..13248875 --- /dev/null +++ b/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 47bdc9a98b239b3d1e93014d3b7a18e23cd70b5c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 158/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSDisplacementMapShader.md | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Set-OBSDisplacementMapShader.md diff --git a/docs/Set-OBSDisplacementMapShader.md b/docs/Set-OBSDisplacementMapShader.md new file mode 100644 index 00000000..311c6f56 --- /dev/null +++ b/docs/Set-OBSDisplacementMapShader.md @@ -0,0 +1,102 @@ +Get-OBSDisplacementMapShader +---------------------------- + +### Synopsis + +Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **DisplacementInfo** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[string]`|false |0 |false |displacement_info| + +#### **DisplacementX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |1 |false |displacement_x| + +#### **DisplacementY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |2 |false |displacement_y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **MaskLayer** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[string]`|false |3 |false |mask_layer| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSDisplacementMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From 917a6a18d3fd228d3adf387b9e8942d8fc2c4b13 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:15 +0000 Subject: [PATCH 159/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Set-OBSDisplacementMapShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSDisplacementMapShader.json diff --git a/docs/_data/Help/Set-OBSDisplacementMapShader.json b/docs/_data/Help/Set-OBSDisplacementMapShader.json new file mode 100644 index 00000000..82fe9189 --- /dev/null +++ b/docs/_data/Help/Set-OBSDisplacementMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From a6cbc093fc9a2edaf04397599f57122390d7415b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:18 +0000 Subject: [PATCH 160/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSGlitchPeriodicShader.md | 102 ++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/Set-OBSGlitchPeriodicShader.md diff --git a/docs/Set-OBSGlitchPeriodicShader.md b/docs/Set-OBSGlitchPeriodicShader.md new file mode 100644 index 00000000..330bc10b --- /dev/null +++ b/docs/Set-OBSGlitchPeriodicShader.md @@ -0,0 +1,102 @@ +Get-OBSGlitchPeriodicShader +--------------------------- + +### Synopsis + +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **AMPL** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **DURA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **SCRA** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +``` From 183f2d18b6ee168768907ca2cb90b180aa6b5c8f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:19 +0000 Subject: [PATCH 161/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Set-OBSGlitchPeriodicShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSGlitchPeriodicShader.json diff --git a/docs/_data/Help/Set-OBSGlitchPeriodicShader.json b/docs/_data/Help/Set-OBSGlitchPeriodicShader.json new file mode 100644 index 00000000..08609385 --- /dev/null +++ b/docs/_data/Help/Set-OBSGlitchPeriodicShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 9f63886eade554f837b8a0d4bba2b3a3f9367f1d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:19 +0000 Subject: [PATCH 162/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSHardBlinkShader.md | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Set-OBSHardBlinkShader.md b/docs/Set-OBSHardBlinkShader.md index 9be98bd9..63748d2e 100644 --- a/docs/Set-OBSHardBlinkShader.md +++ b/docs/Set-OBSHardBlinkShader.md @@ -2,7 +2,6 @@ Get-OBSHardBlinkShader ---------------------- ### Synopsis - Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] < ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Timeoff** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **Timeon** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSHardBlinkShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 03629d6a3cb27743e897173be693c1a37ce8a8eb Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:19 +0000 Subject: [PATCH 163/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSHardBlinkShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSHardBlinkShader.json b/docs/_data/Help/Set-OBSHardBlinkShader.json index 43665759..cf41ebe3 100644 --- a/docs/_data/Help/Set-OBSHardBlinkShader.json +++ b/docs/_data/Help/Set-OBSHardBlinkShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSHardBlinkShader [[-Timeon] ] [[-Timeoff] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 0480a6eef46d85b8391b3de3a7585932bb747437 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:20 +0000 Subject: [PATCH 164/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSMotionBlurShader.md | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/Set-OBSMotionBlurShader.md b/docs/Set-OBSMotionBlurShader.md index 6788858e..0291ea14 100644 --- a/docs/Set-OBSMotionBlurShader.md +++ b/docs/Set-OBSMotionBlurShader.md @@ -2,7 +2,6 @@ Get-OBSMotionBlurShader ----------------------- ### Synopsis - Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,57 +13,57 @@ Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-So ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |3 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PreviousOutput** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------| -|`[string]`|false |0 |false |previous_output| +|`[String]`|false |named |False |previous_output| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |4 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -80,11 +79,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSMotionBlurShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From db24a531f5a74a3c3f2ecc1f2d9cdffa2dd14cc4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:20 +0000 Subject: [PATCH 165/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSMotionBlurShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSMotionBlurShader.json b/docs/_data/Help/Set-OBSMotionBlurShader.json index eb301da6..183df040 100644 --- a/docs/_data/Help/Set-OBSMotionBlurShader.json +++ b/docs/_data/Help/Set-OBSMotionBlurShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSMotionBlurShader [[-PreviousOutput] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From b906428695a8894a37fb886e32f9a91fa4e19a9f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:20 +0000 Subject: [PATCH 166/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSNoiseShader.md | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Set-OBSNoiseShader.md diff --git a/docs/Set-OBSNoiseShader.md b/docs/Set-OBSNoiseShader.md new file mode 100644 index 00000000..6fa983dc --- /dev/null +++ b/docs/Set-OBSNoiseShader.md @@ -0,0 +1,108 @@ +Get-OBSNoiseShader +------------------ + +### Synopsis + +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |4 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Monochromatic** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoiseLevel** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |2 |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **Scale** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |1 |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |5 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| + +#### **Speed** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **UseRand** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------| +|`[switch]`|false |Named |false |use_rand| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +``` From 4f6a8fc50bf4ed021a32972e14a65efd5bc1e66c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:20 +0000 Subject: [PATCH 167/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSNoiseShader.json | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSNoiseShader.json diff --git a/docs/_data/Help/Set-OBSNoiseShader.json b/docs/_data/Help/Set-OBSNoiseShader.json new file mode 100644 index 00000000..db2627eb --- /dev/null +++ b/docs/_data/Help/Set-OBSNoiseShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 682d08e3951cc0cf36e31d9d263e72a2b389f267 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:21 +0000 Subject: [PATCH 168/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSNormalMapShader.md | 114 +++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/Set-OBSNormalMapShader.md diff --git a/docs/Set-OBSNormalMapShader.md b/docs/Set-OBSNormalMapShader.md new file mode 100644 index 00000000..ef22fc4d --- /dev/null +++ b/docs/Set-OBSNormalMapShader.md @@ -0,0 +1,114 @@ +Get-OBSNormalMapShader +---------------------- + +### Synopsis + +Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |3 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertG** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertH** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **InvertR** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **OffsetHeight** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |4 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |2 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |0 |false | + +#### **Type** + +|Type |Required|Position|PipelineInput| +|-------|--------|--------|-------------| +|`[int]`|false |1 |false | + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSNormalMapShader; CommonParameters=True; parameter=System.Object[]}} +``` From 17b3df4c41737a69bcbc6a67876ba927a0697771 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:21 +0000 Subject: [PATCH 169/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSNormalMapShader.json | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSNormalMapShader.json diff --git a/docs/_data/Help/Set-OBSNormalMapShader.json b/docs/_data/Help/Set-OBSNormalMapShader.json new file mode 100644 index 00000000..31f25d5e --- /dev/null +++ b/docs/_data/Help/Set-OBSNormalMapShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 80b810d3855c6eb5f5e8081be69b621e670bb63a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:21 +0000 Subject: [PATCH 170/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSPerspectiveShader.md | 43 +++++++++++++------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Set-OBSPerspectiveShader.md b/docs/Set-OBSPerspectiveShader.md index b39795f2..3288693b 100644 --- a/docs/Set-OBSPerspectiveShader.md +++ b/docs/Set-OBSPerspectiveShader.md @@ -2,7 +2,6 @@ Get-OBSPerspectiveShader ------------------------ ### Synopsis - Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,79 +15,79 @@ Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [-ShowBorder ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 792c746633415f0446e7b734c548542e531808c1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:21 +0000 Subject: [PATCH 171/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSPerspectiveShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSPerspectiveShader.json b/docs/_data/Help/Set-OBSPerspectiveShader.json index 43953a39..c0615f02 100644 --- a/docs/_data/Help/Set-OBSPerspectiveShader.json +++ b/docs/_data/Help/Set-OBSPerspectiveShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSPerspectiveShader [[-AngleX] ] [[-AngleY] ] [[-AngleZ] ] [[-Perspective] ] [[-BorderColor] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From fdb9ba848c2e227ca04b8b36edb54e38bd23e74d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:22 +0000 Subject: [PATCH 172/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSQuadrilateralCropShader.md | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/Set-OBSQuadrilateralCropShader.md diff --git a/docs/Set-OBSQuadrilateralCropShader.md b/docs/Set-OBSQuadrilateralCropShader.md new file mode 100644 index 00000000..611c85ce --- /dev/null +++ b/docs/Set-OBSQuadrilateralCropShader.md @@ -0,0 +1,126 @@ +Get-OBSQuadrilateralCropShader +------------------------------ + +### Synopsis + +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **BottomLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |4 |false |Bottom_Left_X| + +#### **BottomLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-------------| +|`[float]`|false |5 |false |Bottom_Left_Y| + +#### **BottomRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |6 |false |Bottom_Right_X| + +#### **BottomRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|--------------| +|`[float]`|false |7 |false |Bottom_Right_Y| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |9 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |10 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| + +#### **TopLeftX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |0 |false |Top_Left_X| + +#### **TopLeftY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|----------| +|`[float]`|false |1 |false |Top_Left_Y| + +#### **TopRightX** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |2 |false |Top_Right_X| + +#### **TopRightY** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|-----------| +|`[float]`|false |3 |false |Top_Right_Y| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +``` From e609001382cbed2c0b3dce1a6244fcbea54aec9e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:22 +0000 Subject: [PATCH 173/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Set-OBSQuadrilateralCropShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSQuadrilateralCropShader.json diff --git a/docs/_data/Help/Set-OBSQuadrilateralCropShader.json b/docs/_data/Help/Set-OBSQuadrilateralCropShader.json new file mode 100644 index 00000000..e0609f0e --- /dev/null +++ b/docs/_data/Help/Set-OBSQuadrilateralCropShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 1c48ee681e85b8cd7466c27ec5d0b62680699afe Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:22 +0000 Subject: [PATCH 174/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSRepeatGridCenterCropShader.md | 45 ++++++++++------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/Set-OBSRepeatGridCenterCropShader.md b/docs/Set-OBSRepeatGridCenterCropShader.md index bd68baeb..dd7f8e8a 100644 --- a/docs/Set-OBSRepeatGridCenterCropShader.md +++ b/docs/Set-OBSRepeatGridCenterCropShader.md @@ -2,7 +2,6 @@ Get-OBSRepeatGridCenterCropShader --------------------------------- ### Synopsis - Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **Columns** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |6 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Image** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[string]`|false |1 |false | +|`[String]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Rows** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |4 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |7 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |5 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ViewProj** -|Type |Required|Position|PipelineInput| -|-------------|--------|--------|-------------| -|`[float[][]]`|false |0 |false | +|Type |Required|Position|PipelineInput| +|---------------------|--------|--------|-------------| +|`[System.Single[][]]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSRepeatGridCenterCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 5c1d11bbf73814186a28a82140777d9550cc7d80 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:22 +0000 Subject: [PATCH 175/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json b/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json index c4ac45ff..25e86ac4 100644 --- a/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json +++ b/docs/_data/Help/Set-OBSRepeatGridCenterCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSRepeatGridCenterCropShader [[-ViewProj] ] [[-Image] ] [[-Alpha] ] [[-Columns] ] [[-Rows] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From f357261eec50f85d3328094567221e4573017335 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:26 +0000 Subject: [PATCH 176/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSWalkingDeadPixelFixerShader.md | 51 ++++++++++------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Set-OBSWalkingDeadPixelFixerShader.md b/docs/Set-OBSWalkingDeadPixelFixerShader.md index fb0a4df0..dab40694 100644 --- a/docs/Set-OBSWalkingDeadPixelFixerShader.md +++ b/docs/Set-OBSWalkingDeadPixelFixerShader.md @@ -2,7 +2,6 @@ Get-OBSWalkingDeadPixelFixerShader ---------------------------------- ### Synopsis - Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,103 +15,103 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[ |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ContrastThreshold** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------------| -|`[float]`|false |4 |false |Contrast_Threshold| +|`[Float]`|false |named |False |Contrast_Threshold| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |8 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaxClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |6 |false |Max_Cluster_Size| +|`[Int]`|false |named |False |Max_Cluster_Size| #### **MinClusterSize** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------------| -|`[int]`|false |5 |false |Min_Cluster_Size| +|`[Int]`|false |named |False |Min_Cluster_Size| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ScanHeight** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-----------| -|`[int]`|false |1 |false |Scan_Height| +|`[Int]`|false |named |False |Scan_Height| #### **ScanOffsetX** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |2 |false |Scan_Offset_X| +|`[Int]`|false |named |False |Scan_Offset_X| #### **ScanOffsetY** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|-------------| -|`[int]`|false |3 |false |Scan_Offset_Y| +|`[Int]`|false |named |False |Scan_Offset_Y| #### **ScanWidth** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|----------| -|`[int]`|false |0 |false |Scan_Width| +|`[Int]`|false |named |False |Scan_Width| #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |9 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **ShowBorder** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |Show_Border| +|`[Switch]`|false |named |False |Show_Border| #### **ShowGreen** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[switch]`|false |Named |false |Show_Green| +|`[Switch]`|false |named |False |Show_Green| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |7 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -128,11 +127,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSWalkingDeadPixelFixerShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [-ShowBorder ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [-ShowGreen ] [-Bypass ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From f0fe17536a98815e592586c5cae6777f25cd17ac Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:26 +0000 Subject: [PATCH 177/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json b/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json index 858f3e99..64dbd980 100644 --- a/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json +++ b/docs/_data/Help/Set-OBSWalkingDeadPixelFixerShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] ] [[-ScanHeight] ] [[-ScanOffsetX] ] [[-ScanOffsetY] ] [[-ContrastThreshold] ] [[-MinClusterSize] ] [[-MaxClusterSize] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From c77701c59d0805394d2f71ae15976edaa5cf780c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:26 +0000 Subject: [PATCH 178/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/Set-OBSZoomBlurTransitionShader.md | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/Set-OBSZoomBlurTransitionShader.md diff --git a/docs/Set-OBSZoomBlurTransitionShader.md b/docs/Set-OBSZoomBlurTransitionShader.md new file mode 100644 index 00000000..5859f433 --- /dev/null +++ b/docs/Set-OBSZoomBlurTransitionShader.md @@ -0,0 +1,108 @@ +Get-OBSZoomBlurTransitionShader +------------------------------- + +### Synopsis + +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] + +--- + +### Description + +--- + +### Parameters +#### **ConvertLinear** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|--------------| +|`[switch]`|false |Named |false |convert_linear| + +#### **FilterName** + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[string]`|false |5 |true (ByPropertyName)| + +#### **Force** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ImageA** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |0 |false |image_a| + +#### **ImageB** + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[string]`|false |1 |false |image_b| + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **PassThru** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +#### **ShaderText** + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[string]`|false |6 |false |ShaderContent| + +#### **SourceName** + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| + +#### **Strength** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[float]`|false |3 |false | + +#### **TransitionTime** + +|Type |Required|Position|PipelineInput|Aliases | +|---------|--------|--------|-------------|---------------| +|`[float]`|false |2 |false |transition_time| + +#### **UseShaderTime** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[switch]`|false |Named |false | + +--- + +### Inputs +System.String + +--- + +### Outputs +* [Object](https://learn.microsoft.com/en-us/dotnet/api/System.Object) + +--- + +### Syntax +```PowerShell +syntaxItem +``` +```PowerShell +---------- +``` +```PowerShell +{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +``` From f2956c640fb907ba5d7a31bcb2becc4e919c59b1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:26 +0000 Subject: [PATCH 179/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- .../Help/Set-OBSZoomBlurTransitionShader.json | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/_data/Help/Set-OBSZoomBlurTransitionShader.json diff --git a/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json new file mode 100644 index 00000000..c14ca126 --- /dev/null +++ b/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json @@ -0,0 +1,33 @@ +{ + "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Description": "", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + "System.String\n" + ], + "Outputs": [ + "System.Object" + ], + "Links": [], + "Examples": [] +} \ No newline at end of file From 41912957f8b6cdbf14a96c62833d82e01bf3fdaa Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:55:57 +0000 Subject: [PATCH 180/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- docs/obs-powershell-commands.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/obs-powershell-commands.md b/docs/obs-powershell-commands.md index 131c7af3..3d678294 100644 --- a/docs/obs-powershell-commands.md +++ b/docs/obs-powershell-commands.md @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 787 commands -(316 functions and 471 aliases) +obs-powershell exports 808 commands +(323 functions and 485 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](obs-powershell-websocket-commands.md). @@ -25,6 +25,7 @@ Functions |[Copy-OBSSceneItem](Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | |[Disconnect-OBS](Disconnect-OBS.md) |Disconnects OBS | |[Get-OBS](Get-OBS.md) |Gets OBS | +|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | |[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | @@ -53,6 +54,7 @@ Functions |[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | |[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | |[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | |[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | @@ -106,6 +108,7 @@ Functions |[Get-OBSGroup](Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | |[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | |[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | |[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | |[Get-OBSHotkey](Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | @@ -131,6 +134,7 @@ Functions |[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | |[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | |[Get-OBSMonitor](Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | +|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | |[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | @@ -141,6 +145,7 @@ Functions |[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | |[Get-OBSPersistentData](Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | +|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | |[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | @@ -155,6 +160,7 @@ Functions |[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | |[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | |[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | @@ -220,6 +226,7 @@ Functions |[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | |[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | |[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | |[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](Get-OBSZoomShader.md) | @@ -350,6 +357,7 @@ Aliases |[Copy-OBSSceneItem](Copy-OBSSceneItem.md) | |[Disconnect-OBS](Disconnect-OBS.md) | |[Get-OBS](Get-OBS.md) | +|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | |[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | |[Get-OBSAddShader](Get-OBSAddShader.md) | |[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | @@ -378,6 +386,7 @@ Aliases |[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | |[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | |[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | |[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) | |[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) | |[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) | @@ -431,6 +440,7 @@ Aliases |[Get-OBSGroup](Get-OBSGroup.md) | |[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) | |[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | |[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | |[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | |[Get-OBSHotkey](Get-OBSHotkey.md) | @@ -456,6 +466,7 @@ Aliases |[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | |[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) | |[Get-OBSMonitor](Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | |[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | |[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | |[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | @@ -466,6 +477,7 @@ Aliases |[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | |[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | |[Get-OBSPersistentData](Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | |[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | |[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | |[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | @@ -480,6 +492,7 @@ Aliases |[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | |[Get-OBSReflectShader](Get-OBSReflectShader.md) | |[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | |[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | |[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | |[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) | @@ -545,6 +558,7 @@ Aliases |[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | |[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) | |[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | |[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | |[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | |[Get-OBSZoomShader](Get-OBSZoomShader.md) | From 312ac9c03e0d19b8a4d7effdff4b6f251aaff341 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:56:07 +0000 Subject: [PATCH 181/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- obs-powershell-Help.xml | 25812 ++++++++++++++++++++++---------------- 1 file changed, 15073 insertions(+), 10739 deletions(-) diff --git a/obs-powershell-Help.xml b/obs-powershell-Help.xml index 300200e1..e7441f79 100644 --- a/obs-powershell-Help.xml +++ b/obs-powershell-Help.xml @@ -1443,9 +1443,7 @@ This can increase performance, and also silently ignore critical errorsOBS3dPanelShader Get - -Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-TiltXDeg] <float>] [[-TiltYDeg] <float>] [[-TiltZDeg] <float>] [[-PosX] <float>] [[-PosY] <float>] [[-Thickness] <float>] [[-RadiusFb] <float>] [[-Brightness] <float>] [[-LightPosition] <int>] [[-Wiggle] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-TiltXDeg] <float>] [[-TiltYDeg] <float>] [[-TiltZDeg] <float>] [[-PosX] <float>] [[-PosY] <float>] [[-Thickness] <float>] [[-RadiusFb] <float>] [[-Brightness] <float>] [[-LightPosition] <int>] [[-Wiggle] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -1457,6 +1455,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Credits + + String @@ -1469,6 +1469,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Scale + + Float @@ -1481,6 +1483,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltXDeg + + Float @@ -1493,6 +1497,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltYDeg + + Float @@ -1505,6 +1511,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltZDeg + + Float @@ -1517,6 +1525,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PosX + + Float @@ -1529,6 +1539,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PosY + + Float @@ -1541,6 +1553,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Thickness + + Float @@ -1553,6 +1567,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til RadiusFb + + Float @@ -1565,6 +1581,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Brightness + + Float @@ -1577,6 +1595,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til LightPosition + + Int @@ -1589,6 +1609,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Wiggle + + Float @@ -1601,6 +1623,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til WiggleRot + + Switch @@ -1613,6 +1637,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til SourceName + + String @@ -1625,6 +1651,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til FilterName + + String @@ -1637,6 +1665,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til ShaderText + + String @@ -1649,6 +1679,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Force + + Switch @@ -1661,6 +1693,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PassThru + + Switch @@ -1673,6 +1707,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til NoResponse + + Switch @@ -1685,6 +1721,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til UseShaderTime + + Switch @@ -1700,6 +1738,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Brightness + + Float @@ -1712,6 +1752,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Credits + + String @@ -1724,6 +1766,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til FilterName + + String @@ -1736,6 +1780,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Force + + Switch @@ -1748,6 +1794,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til LightPosition + + Int @@ -1760,6 +1808,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til NoResponse + + Switch @@ -1772,6 +1822,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PassThru + + Switch @@ -1784,6 +1836,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PosX + + Float @@ -1796,6 +1850,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til PosY + + Float @@ -1808,6 +1864,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til RadiusFb + + Float @@ -1820,6 +1878,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Scale + + Float @@ -1832,6 +1892,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til ShaderText + + String @@ -1844,6 +1906,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til SourceName + + String @@ -1856,6 +1920,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Thickness + + Float @@ -1868,6 +1934,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltXDeg + + Float @@ -1880,6 +1948,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltYDeg + + Float @@ -1892,6 +1962,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til TiltZDeg + + Float @@ -1904,6 +1976,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til UseShaderTime + + Switch @@ -1916,6 +1990,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til Wiggle + + Float @@ -1928,6 +2004,8 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til WiggleRot + + Switch @@ -5789,6 +5867,286 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til + + + Get-OBSAudioShader + OBSAudioShader + Get + + +Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float>] [[-Intensity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBSAudioShader + + AudioPeak + + + Float + + Float + + + + + + + AudioMagnitude + + + Float + + Float + + + + + + + Intensity + + + Float + + Float + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + AudioMagnitude + + + Float + + Float + + + + + + + AudioPeak + + + Float + + Float + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + Intensity + + + Float + + Float + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + ShaderText + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBSBackgroundRemovalShader @@ -14311,9 +14669,7 @@ Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-Til OBSCubeRotatingShader Get - -Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Shadow] <float>] [[-OtherImage1] <string>] [[-OtherImage2] <string>] [[-OtherImage3] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Shadow] <float>] [[-OtherImage1] <string>] [[-OtherImage2] <string>] [[-OtherImage3] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -14325,6 +14681,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Images + + Int @@ -14337,6 +14695,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Speed + + Float @@ -14349,6 +14709,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Shadow + + Float @@ -14361,6 +14723,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage1 + + String @@ -14373,6 +14737,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage2 + + String @@ -14385,6 +14751,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage3 + + String @@ -14397,6 +14765,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh SourceName + + String @@ -14409,6 +14779,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh FilterName + + String @@ -14421,6 +14793,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh ShaderText + + String @@ -14433,6 +14807,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Force + + Switch @@ -14445,6 +14821,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh PassThru + + Switch @@ -14457,6 +14835,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh NoResponse + + Switch @@ -14469,6 +14849,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh UseShaderTime + + Switch @@ -14484,6 +14866,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh FilterName + + String @@ -14496,6 +14880,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Force + + Switch @@ -14508,6 +14894,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Images + + Int @@ -14520,6 +14908,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh NoResponse + + Switch @@ -14532,6 +14922,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage1 + + String @@ -14544,6 +14936,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage2 + + String @@ -14556,6 +14950,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh OtherImage3 + + String @@ -14568,6 +14964,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh PassThru + + Switch @@ -14580,6 +14978,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh ShaderText + + String @@ -14592,6 +14992,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Shadow + + Float @@ -14604,6 +15006,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh SourceName + + String @@ -14616,6 +15020,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh Speed + + Float @@ -14628,6 +15034,8 @@ Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Sh UseShaderTime + + Switch @@ -18339,11 +18747,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDivideRotateShader - OBSDivideRotateShader + Get-OBSDisplacementMapAdvancedInvertShader + OBSDisplacementMapAdvancedInvertShader Get - Get-OBSDivideRotateShader [[-IChannel0] <string>] [[-SpeedPercentage] <int>] [[-AlphaPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -18351,12 +18761,10 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDivideRotateShader + Get-OBSDisplacementMapAdvancedInvertShader - IChannel0 + DisplacementInfo - - String @@ -18367,24 +18775,32 @@ This can increase performance, and also silently ignore critical errors - SpeedPercentage + DisplacementX - - - Int + Float - Int + Float - AlphaPercentage + DisplacementY + + + Float + + Float + + + + + + + DisplacementCurve - - Int @@ -18394,11 +18810,129 @@ This can increase performance, and also silently ignore critical errors + + BlurInfo + + + String + + String + + + + + + + BlurSize + + + Float + + Float + + + + + + + BlurQuality + + + Float + + Float + + + + + + + BlurDirections + + + Float + + Float + + + + + + + BlurAngle + + + Float + + Float + + + + + + + ChromaticAberrationInfo + + + String + + String + + + + + + + ChromaticAberration + + + Float + + Float + + + + + + + ColorizeInfo + + + String + + String + + + + + + + ColorizeColor + + + String + + String + + + + + + + FlagsInfo + + + String + + String + + + + + - ApplyToAlphaLayer + BlueAffectsStrength - - Switch @@ -18408,11 +18942,57 @@ This can increase performance, and also silently ignore critical errors - - Notes + + BlueAffectsColorize + + + Switch + + Switch + + + + + + + BlueAffectsBlur + + + Switch + + Switch + + + + + + + AlphaAffectsStrength + + + Switch + + Switch + + + + + + + ApplyAlpha + + + Switch + + Switch + + + + + + + BackgroundLayer - - String @@ -18422,11 +19002,9 @@ This can increase performance, and also silently ignore critical errors - + SourceName - - String @@ -18436,11 +19014,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -18450,11 +19026,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -18467,8 +19041,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -18481,8 +19053,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -18495,8 +19065,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -18509,8 +19077,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -18524,24 +19090,20 @@ This can increase performance, and also silently ignore critical errors - AlphaPercentage + AlphaAffectsStrength - - - Int + Switch - Int + Switch - ApplyToAlphaLayer + ApplyAlpha - - Switch @@ -18552,10 +19114,8 @@ This can increase performance, and also silently ignore critical errors - FilterName + BackgroundLayer - - String @@ -18566,10 +19126,8 @@ This can increase performance, and also silently ignore critical errors - Force + BlueAffectsBlur - - Switch @@ -18580,24 +19138,20 @@ This can increase performance, and also silently ignore critical errors - IChannel0 + BlueAffectsColorize - - - String + Switch - String + Switch - NoResponse + BlueAffectsStrength - - Switch @@ -18608,10 +19162,32 @@ This can increase performance, and also silently ignore critical errors - Notes + BlurAngle + + + Float + + Float + + + + + + + BlurDirections + + + Float + + Float + + + + + + + BlurInfo - - String @@ -18622,24 +19198,44 @@ This can increase performance, and also silently ignore critical errors - PassThru + BlurQuality - - - Switch + Float - Switch + Float - ShaderText + BlurSize + + + Float + + Float + + + + + + + ChromaticAberration + + + Float + + Float + + + + + + + ChromaticAberrationInfo - - String @@ -18650,10 +19246,8 @@ This can increase performance, and also silently ignore critical errors - SourceName + ColorizeColor - - String @@ -18664,10 +19258,20 @@ This can increase performance, and also silently ignore critical errors - SpeedPercentage + ColorizeInfo + + + String + + String + + + + + + + DisplacementCurve - - Int @@ -18677,11 +19281,129 @@ This can increase performance, and also silently ignore critical errors + + DisplacementInfo + + + String + + String + + + + + + + DisplacementX + + + Float + + Float + + + + + + + DisplacementY + + + Float + + Float + + + + + + + FilterName + + + String + + String + + + + + + + FlagsInfo + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + ShaderText + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + UseShaderTime - - Switch @@ -18713,11 +19435,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDoodleShader - OBSDoodleShader + Get-OBSDisplacementMapAdvancedShader + OBSDisplacementMapAdvancedShader Get - Get-OBSDoodleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-DoodleScalePercent] <float>] [[-SnapPercent] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -18725,40 +19449,34 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDoodleShader + Get-OBSDisplacementMapAdvancedShader - ViewProj + DisplacementInfo - - - System.Single[][] + String - System.Single[][] + String - Image + DisplacementX - - - String + Float - String + Float - ElapsedTime + DisplacementY - - Float @@ -18769,52 +19487,44 @@ This can increase performance, and also silently ignore critical errors - UvOffset + DisplacementCurve - - - System.Single[] + Int - System.Single[] + Int - UvScale + BlurInfo - - - System.Single[] + String - System.Single[] + String - UvPixelInterval + BlurSize - - - System.Single[] + Float - System.Single[] + Float - RandF + BlurQuality - - Float @@ -18825,24 +19535,20 @@ This can increase performance, and also silently ignore critical errors - UvSize + BlurDirections - - - System.Single[] + Float - System.Single[] + Float - DoodleScalePercent + BlurAngle - - Float @@ -18853,10 +19559,20 @@ This can increase performance, and also silently ignore critical errors - SnapPercent + ChromaticAberrationInfo + + + String + + String + + + + + + + ChromaticAberration - - Float @@ -18866,11 +19582,9 @@ This can increase performance, and also silently ignore critical errors - - Notes + + ColorizeInfo - - String @@ -18880,11 +19594,105 @@ This can increase performance, and also silently ignore critical errors - + + ColorizeColor + + + String + + String + + + + + + + FlagsInfo + + + String + + String + + + + + + + BlueAffectsStrength + + + Switch + + Switch + + + + + + + BlueAffectsColorize + + + Switch + + Switch + + + + + + + BlueAffectsBlur + + + Switch + + Switch + + + + + + + AlphaAffectsStrength + + + Switch + + Switch + + + + + + + ApplyAlpha + + + Switch + + Switch + + + + + + + MaskLayer + + + String + + String + + + + + + SourceName - - String @@ -18894,11 +19702,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -18908,11 +19714,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -18925,8 +19729,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -18939,8 +19741,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -18953,8 +19753,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -18967,8 +19765,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -18982,10 +19778,68 @@ This can increase performance, and also silently ignore critical errors - DoodleScalePercent + AlphaAffectsStrength + + + Switch + + Switch + + + + + + + ApplyAlpha + + + Switch + + Switch + + + + + + + BlueAffectsBlur + + + Switch + + Switch + + + + + + + BlueAffectsColorize + + + Switch + + Switch + + + + + + + BlueAffectsStrength + + + Switch + + Switch + + + + + + + BlurAngle - - Float @@ -18996,10 +19850,8 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + BlurDirections - - Float @@ -19010,10 +19862,8 @@ This can increase performance, and also silently ignore critical errors - FilterName + BlurInfo - - String @@ -19024,52 +19874,44 @@ This can increase performance, and also silently ignore critical errors - Force + BlurQuality - - - Switch + Float - Switch + Float - Image + BlurSize - - - String + Float - String + Float - NoResponse + ChromaticAberration - - - Switch + Float - Switch + Float - Notes + ChromaticAberrationInfo - - String @@ -19080,38 +19922,44 @@ This can increase performance, and also silently ignore critical errors - PassThru + ColorizeColor - - - Switch + String - Switch + String - RandF + ColorizeInfo - - - Float + String - Float + String - ShaderText + DisplacementCurve + + + Int + + Int + + + + + + + DisplacementInfo - - String @@ -19122,10 +19970,8 @@ This can increase performance, and also silently ignore critical errors - SnapPercent + DisplacementX - - Float @@ -19136,10 +19982,20 @@ This can increase performance, and also silently ignore critical errors - SourceName + DisplacementY + + + Float + + Float + + + + + + + FilterName - - String @@ -19150,10 +20006,20 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + FlagsInfo + + + String + + String + + + + + + + Force - - Switch @@ -19164,70 +20030,72 @@ This can increase performance, and also silently ignore critical errors - UvOffset + MaskLayer - - - System.Single[] + String - System.Single[] + String - UvPixelInterval + NoResponse - - - System.Single[] + Switch - System.Single[] + Switch - UvScale + PassThru - - - System.Single[] + Switch - System.Single[] + Switch - UvSize + ShaderText - - - System.Single[] + String - System.Single[] + String - ViewProj + SourceName - - - System.Single[][] + String - System.Single[][] + String + + + + + + + UseShaderTime + + + Switch + + Switch @@ -19255,11 +20123,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDrawingsShader - OBSDrawingsShader + Get-OBSDisplacementMapInvertShader + OBSDisplacementMapInvertShader Get - Get-OBSDrawingsShader [[-AngleNum] <int>] [[-SampNum] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -19267,40 +20137,58 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDrawingsShader + Get-OBSDisplacementMapInvertShader - AngleNum + DisplacementInfo - - - Int + String - Int + String - SampNum + DisplacementX - - - Int + Float - Int + Float - + + DisplacementY + + + Float + + Float + + + + + + + BackgroundLayer + + + String + + String + + + + + + SourceName - - String @@ -19310,11 +20198,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -19324,11 +20210,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -19341,8 +20225,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -19355,8 +20237,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -19369,8 +20249,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -19383,8 +20261,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -19398,24 +20274,20 @@ This can increase performance, and also silently ignore critical errors - AngleNum + BackgroundLayer - - - Int + String - Int + String - FilterName + DisplacementInfo - - String @@ -19426,24 +20298,44 @@ This can increase performance, and also silently ignore critical errors - Force + DisplacementX - - - Switch + Float - Switch + Float - NoResponse + DisplacementY + + + Float + + Float + + + + + + + FilterName + + + String + + String + + + + + + + Force - - Switch @@ -19454,10 +20346,8 @@ This can increase performance, and also silently ignore critical errors - PassThru + NoResponse - - Switch @@ -19468,14 +20358,12 @@ This can increase performance, and also silently ignore critical errors - SampNum + PassThru - - - Int + Switch - Int + Switch @@ -19484,8 +20372,6 @@ This can increase performance, and also silently ignore critical errors ShaderText - - String @@ -19498,8 +20384,6 @@ This can increase performance, and also silently ignore critical errors SourceName - - String @@ -19512,8 +20396,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -19545,11 +20427,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDropShadowShader - OBSDropShadowShader + Get-OBSDisplacementMapShader + OBSDisplacementMapShader Get - Get-OBSDropShadowShader [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-Notes] <string>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -19557,68 +20441,46 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDropShadowShader + Get-OBSDisplacementMapShader - ShadowOffsetX + DisplacementInfo - - - Int + String - Int + String - ShadowOffsetY + DisplacementX - - - Int + Float - Int + Float - ShadowBlurSize + DisplacementY - - - Int + Float - Int + Float - Notes - - - - - String - - String - - - - - - - ShadowColor + MaskLayer - - String @@ -19628,25 +20490,9 @@ This can increase performance, and also silently ignore critical errors - - IsAlphaPremultiplied - - - - - Switch - - Switch - - - - - - + SourceName - - String @@ -19656,11 +20502,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -19670,11 +20514,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -19687,8 +20529,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -19701,8 +20541,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -19715,8 +20553,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -19729,8 +20565,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -19744,10 +20578,8 @@ This can increase performance, and also silently ignore critical errors - FilterName + DisplacementInfo - - String @@ -19758,52 +20590,32 @@ This can increase performance, and also silently ignore critical errors - Force - - - - - Switch - - Switch - - - - - - - IsAlphaPremultiplied + DisplacementX - - - Switch + Float - Switch + Float - NoResponse + DisplacementY - - - Switch + Float - Switch + Float - Notes + FilterName - - String @@ -19814,10 +20626,8 @@ This can increase performance, and also silently ignore critical errors - PassThru + Force - - Switch @@ -19828,10 +20638,8 @@ This can increase performance, and also silently ignore critical errors - ShaderText + MaskLayer - - String @@ -19842,56 +20650,36 @@ This can increase performance, and also silently ignore critical errors - ShadowBlurSize - - - - - Int - - Int - - - - - - - ShadowColor + NoResponse - - - String + Switch - String + Switch - ShadowOffsetX + PassThru - - - Int + Switch - Int + Switch - ShadowOffsetY + ShaderText - - - Int + String - Int + String @@ -19900,8 +20688,6 @@ This can increase performance, and also silently ignore critical errors SourceName - - String @@ -19914,8 +20700,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -19947,11 +20731,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDrunkShader - OBSDrunkShader + Get-OBSDivideRotateShader + OBSDivideRotateShader Get - Get-OBSDrunkShader [[-ColorMatrix] <float[][]>] [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeedPercent] <int>] [[-GlowColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSDivideRotateShader [[-IChannel0] <string>] [[-SpeedPercentage] <int>] [[-AlphaPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -19959,23 +20743,23 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDrunkShader + Get-OBSDivideRotateShader - ColorMatrix + IChannel0 - System.Single[][] + String - System.Single[][] + String - GlowPercent + SpeedPercentage @@ -19989,49 +20773,7 @@ This can increase performance, and also silently ignore critical errors - Blur - - - - - Int - - Int - - - - - - - MinBrightness - - - - - Int - - Int - - - - - - - MaxBrightness - - - - - Int - - Int - - - - - - - PulseSpeedPercent + AlphaPercentage @@ -20058,49 +20800,7 @@ This can increase performance, and also silently ignore critical errors - - GlowColor - - - - - String - - String - - - - - - - Ease - - - - - Switch - - Switch - - - - - - - Glitch - - - - - Switch - - Switch - - - - - - + Notes @@ -20114,7 +20814,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -20128,7 +20828,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -20142,7 +20842,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -20216,21 +20916,7 @@ This can increase performance, and also silently ignore critical errors - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - Blur + AlphaPercentage @@ -20244,21 +20930,7 @@ This can increase performance, and also silently ignore critical errors - ColorMatrix - - - - - System.Single[][] - - System.Single[][] - - - - - - - Ease + ApplyToAlphaLayer @@ -20300,21 +20972,7 @@ This can increase performance, and also silently ignore critical errors - Glitch - - - - - Switch - - Switch - - - - - - - GlowColor + IChannel0 @@ -20327,48 +20985,6 @@ This can increase performance, and also silently ignore critical errors - - GlowPercent - - - - - Int - - Int - - - - - - - MaxBrightness - - - - - Int - - Int - - - - - - - MinBrightness - - - - - Int - - Int - - - - - NoResponse @@ -20412,21 +21028,21 @@ This can increase performance, and also silently ignore critical errors - PulseSpeedPercent + ShaderText - Int + String - Int + String - ShaderText + SourceName @@ -20440,14 +21056,14 @@ This can increase performance, and also silently ignore critical errors - SourceName + SpeedPercentage - String + Int - String + Int @@ -20489,11 +21105,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDynamicMaskShader - OBSDynamicMaskShader + Get-OBSDoodleShader + OBSDoodleShader Get - Get-OBSDynamicMaskShader [[-InputSource] <string>] [[-RedBaseValue] <float>] [[-RedRedInputValue] <float>] [[-RedGreenInputValue] <float>] [[-RedBlueInputValue] <float>] [[-RedAlphaInputValue] <float>] [[-RedMultiplier] <float>] [[-GreenBaseValue] <float>] [[-GreenRedInputValue] <float>] [[-GreenGreenInputValue] <float>] [[-GreenBlueInputValue] <float>] [[-GreenAlphaInputValue] <float>] [[-GreenMultiplier] <float>] [[-BlueBaseValue] <float>] [[-BlueRedInputValue] <float>] [[-BlueGreenInputValue] <float>] [[-BlueBlueInputValue] <float>] [[-BlueAlphaInputValue] <float>] [[-BlueMultiplier] <float>] [[-AlphaBaseValue] <float>] [[-AlphaRedInputValue] <float>] [[-AlphaGreenInputValue] <float>] [[-AlphaBlueInputValue] <float>] [[-AlphaAlphaInputValue] <float>] [[-AlphaMultiplier] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSDoodleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-DoodleScalePercent] <float>] [[-SnapPercent] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -20501,37 +21117,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSDynamicMaskShader + Get-OBSDoodleShader - InputSource + ViewProj - String + System.Single[][] - String + System.Single[][] - RedBaseValue + Image - Float + String - Float + String - RedRedInputValue + ElapsedTime @@ -20545,49 +21161,49 @@ This can increase performance, and also silently ignore critical errors - RedGreenInputValue + UvOffset - Float + System.Single[] - Float + System.Single[] - RedBlueInputValue + UvScale - Float + System.Single[] - Float + System.Single[] - RedAlphaInputValue + UvPixelInterval - Float + System.Single[] - Float + System.Single[] - RedMultiplier + RandF @@ -20601,21 +21217,21 @@ This can increase performance, and also silently ignore critical errors - GreenBaseValue + UvSize - Float + System.Single[] - Float + System.Single[] - GreenRedInputValue + DoodleScalePercent @@ -20629,7 +21245,7 @@ This can increase performance, and also silently ignore critical errors - GreenGreenInputValue + SnapPercent @@ -20643,308 +21259,112 @@ This can increase performance, and also silently ignore critical errors - GreenBlueInputValue + Notes - Float + String - Float + String - - GreenAlphaInputValue + + SourceName - Float + String - Float + String - - GreenMultiplier + + FilterName - Float + String - Float + String - BlueBaseValue - - - - - Float - - Float - - - - - - - BlueRedInputValue + ShaderText - Float + String - Float + String - - BlueGreenInputValue + + Force - Float + Switch - Float + Switch - - BlueBlueInputValue + + PassThru - Float + Switch - Float + Switch - - BlueAlphaInputValue + + NoResponse - Float + Switch - Float + Switch - - BlueMultiplier + + UseShaderTime - Float + Switch - Float - - - - - - - AlphaBaseValue - - - - - Float - - Float - - - - - - - AlphaRedInputValue - - - - - Float - - Float - - - - - - - AlphaGreenInputValue - - - - - Float - - Float - - - - - - - AlphaBlueInputValue - - - - - Float - - Float - - - - - - - AlphaAlphaInputValue - - - - - Float - - Float - - - - - - - AlphaMultiplier - - - - - Float - - Float - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch + Switch @@ -20954,147 +21374,7 @@ This can increase performance, and also silently ignore critical errors - AlphaAlphaInputValue - - - - - Float - - Float - - - - - - - AlphaBaseValue - - - - - Float - - Float - - - - - - - AlphaBlueInputValue - - - - - Float - - Float - - - - - - - AlphaGreenInputValue - - - - - Float - - Float - - - - - - - AlphaMultiplier - - - - - Float - - Float - - - - - - - AlphaRedInputValue - - - - - Float - - Float - - - - - - - BlueAlphaInputValue - - - - - Float - - Float - - - - - - - BlueBaseValue - - - - - Float - - Float - - - - - - - BlueBlueInputValue - - - - - Float - - Float - - - - - - - BlueGreenInputValue - - - - - Float - - Float - - - - - - - BlueMultiplier + DoodleScalePercent @@ -21108,7 +21388,7 @@ This can increase performance, and also silently ignore critical errors - BlueRedInputValue + ElapsedTime @@ -21150,91 +21430,35 @@ This can increase performance, and also silently ignore critical errors - GreenAlphaInputValue - - - - - Float - - Float - - - - - - - GreenBaseValue - - - - - Float - - Float - - - - - - - GreenBlueInputValue - - - - - Float - - Float - - - - - - - GreenGreenInputValue - - - - - Float - - Float - - - - - - - GreenMultiplier + Image - Float + String - Float + String - GreenRedInputValue + NoResponse - Float + Switch - Float + Switch - InputSource + Notes @@ -21248,7 +21472,7 @@ This can increase performance, and also silently ignore critical errors - NoResponse + PassThru @@ -21262,35 +21486,35 @@ This can increase performance, and also silently ignore critical errors - PassThru + RandF - Switch + Float - Switch + Float - RedAlphaInputValue + ShaderText - Float + String - Float + String - RedBaseValue + SnapPercent @@ -21304,98 +21528,98 @@ This can increase performance, and also silently ignore critical errors - RedBlueInputValue + SourceName - Float + String - Float + String - RedGreenInputValue + UseShaderTime - Float + Switch - Float + Switch - RedMultiplier + UvOffset - Float + System.Single[] - Float + System.Single[] - RedRedInputValue + UvPixelInterval - Float + System.Single[] - Float + System.Single[] - ShaderText + UvScale - String + System.Single[] - String + System.Single[] - SourceName + UvSize - String + System.Single[] - String + System.Single[] - UseShaderTime + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] @@ -21423,11 +21647,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEdgeDetectionShader - OBSEdgeDetectionShader + Get-OBSDrawingsShader + OBSDrawingsShader Get - Get-OBSEdgeDetectionShader [[-Sensitivity] <float>] [[-EdgeColor] <string>] [[-NonEdgeColor] <string>] [[-AlphaLevel] <float>] [[-RandF] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertEdge] [-EdgeMultiply] [-NonEdgeMultiply] [-AlphaChannel] [-AlphaInvert] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSDrawingsShader [[-AngleNum] <int>] [[-SampNum] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -21435,37 +21659,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEdgeDetectionShader + Get-OBSDrawingsShader - Sensitivity + AngleNum - Float + Int - Float + Int - - InvertEdge + + SampNum - Switch + Int - Switch + Int - - EdgeColor + + SourceName @@ -21478,22 +21702,22 @@ This can increase performance, and also silently ignore critical errors - - EdgeMultiply + + FilterName - Switch + String - Switch + String - - NonEdgeColor + + ShaderText @@ -21507,7 +21731,7 @@ This can increase performance, and also silently ignore critical errors - NonEdgeMultiply + Force @@ -21521,7 +21745,7 @@ This can increase performance, and also silently ignore critical errors - AlphaChannel + PassThru @@ -21534,22 +21758,8 @@ This can increase performance, and also silently ignore critical errors - - AlphaLevel - - - - - Float - - Float - - - - - - AlphaInvert + NoResponse @@ -21562,24 +21772,230 @@ This can increase performance, and also silently ignore critical errors - - RandF + + UseShaderTime - Float + Switch - Float + Switch - - Notes - - + + + + + AngleNum + + + + + Int + + Int + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + SampNum + + + + + Int + + Int + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSDropShadowShader + OBSDropShadowShader + Get + + Get-OBSDropShadowShader [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-Notes] <string>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSDropShadowShader + + ShadowOffsetX + + + + + Int + + Int + + + + + + + ShadowOffsetY + + + + + Int + + Int + + + + + + + ShadowBlurSize + + + + + Int + + Int + + + + + + + Notes + + String @@ -21590,7 +22006,35 @@ This can increase performance, and also silently ignore critical errors - + + ShadowColor + + + + + String + + String + + + + + + + IsAlphaPremultiplied + + + + + Switch + + Switch + + + + + + SourceName @@ -21604,7 +22048,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -21618,7 +22062,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -21692,49 +22136,7 @@ This can increase performance, and also silently ignore critical errors - AlphaChannel - - - - - Switch - - Switch - - - - - - - AlphaInvert - - - - - Switch - - Switch - - - - - - - AlphaLevel - - - - - Float - - Float - - - - - - - EdgeColor + FilterName @@ -21748,7 +22150,7 @@ This can increase performance, and also silently ignore critical errors - EdgeMultiply + Force @@ -21762,21 +22164,7 @@ This can increase performance, and also silently ignore critical errors - FilterName - - - - - String - - String - - - - - - - Force + IsAlphaPremultiplied @@ -21790,7 +22178,7 @@ This can increase performance, and also silently ignore critical errors - InvertEdge + NoResponse @@ -21804,7 +22192,7 @@ This can increase performance, and also silently ignore critical errors - NonEdgeColor + Notes @@ -21818,21 +22206,7 @@ This can increase performance, and also silently ignore critical errors - NonEdgeMultiply - - - - - Switch - - Switch - - - - - - - NoResponse + PassThru @@ -21846,7 +22220,7 @@ This can increase performance, and also silently ignore critical errors - Notes + ShaderText @@ -21860,56 +22234,56 @@ This can increase performance, and also silently ignore critical errors - PassThru + ShadowBlurSize - Switch + Int - Switch + Int - RandF + ShadowColor - Float + String - Float + String - Sensitivity + ShadowOffsetX - Float + Int - Float + Int - ShaderText + ShadowOffsetY - String + Int - String + Int @@ -21965,76 +22339,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEffect - OBSEffect - Get - - Gets OBS Effects - - 0.2.0.1 - - - Gets effects currently loaded into OBS-PowerShell. - An effect can be thought of as a name with a series of messages to OBS. - Those messages can be defined in a .json file or a script, in any module that tags OBS. - They can also be defined in a function or script named like: - * `*.OBS.FX.*` - * `*.OBS.Effect.*` - * `*.OBS.Effects.*` - - - - Get-OBSEffect - - Name - - The name of the effect. - - String - - String - - - - - - - - - - Name - - The name of the effect. - - String - - String - - - - - - - - - Import-OBSEffect - - - - - Remove-OBSEffect - - - - - - - - Get-OBSEmbersShader - OBSEmbersShader + Get-OBSDrunkShader + OBSDrunkShader Get - Get-OBSEmbersShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-AnimationSpeed] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-MovementSpeedPercent] <int>] [[-LayersCount] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSDrunkShader [[-ColorMatrix] <float[][]>] [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeedPercent] <int>] [[-GlowColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -22042,9 +22351,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEmbersShader + Get-OBSDrunkShader - ViewProj + ColorMatrix @@ -22058,161 +22367,161 @@ This can increase performance, and also silently ignore critical errors - Image + GlowPercent - String + Int - String + Int - ElapsedTime + Blur - Float + Int - Float + Int - UvOffset + MinBrightness - System.Single[] + Int - System.Single[] + Int - UvScale + MaxBrightness - System.Single[] + Int - System.Single[] + Int - UvSize + PulseSpeedPercent - System.Single[] + Int - System.Single[] + Int - - UvPixelInterval + + ApplyToAlphaLayer - System.Single[] + Switch - System.Single[] + Switch - - RandF + + GlowColor - Float + String - Float + String - - RandInstanceF + + Ease - Float + Switch - Float + Switch - - RandActivationF + + Glitch - Float + Switch - Float + Switch - - Loops + + Notes - Int + String - Int + String - - LocalTime + + SourceName - Float + String - Float + String - - Notes + + FilterName @@ -22225,161 +22534,7 @@ This can increase performance, and also silently ignore critical errors - - AnimationSpeed - - - - - Float - - Float - - - - - - - MovementDirectionHorizontal - - - - - Float - - Float - - - - - - - MovementDirectionVertical - - - - - Float - - Float - - - - - - - MovementSpeedPercent - - - - - Int - - Int - - - - - - - LayersCount - - - - - Int - - Int - - - - - - - LumaMin - - - - - Float - - Float - - - - - - - LumaMinSmooth - - - - - Float - - Float - - - - - - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - + ShaderText @@ -22453,56 +22608,56 @@ This can increase performance, and also silently ignore critical errors - AlphaPercentage + ApplyToAlphaLayer - Float + Switch - Float + Switch - AnimationSpeed + Blur - Float + Int - Float + Int - ApplyToAlphaLayer + ColorMatrix - Switch + System.Single[][] - Switch + System.Single[][] - ElapsedTime + Ease - Float + Switch - Float + Switch @@ -22537,49 +22692,35 @@ This can increase performance, and also silently ignore critical errors - Image - - - - - String - - String - - - - - - - LayersCount + Glitch - Int + Switch - Int + Switch - LocalTime + GlowColor - Float + String - Float + String - Loops + GlowPercent @@ -22593,63 +22734,21 @@ This can increase performance, and also silently ignore critical errors - LumaMin - - - - - Float - - Float - - - - - - - LumaMinSmooth - - - - - Float - - Float - - - - - - - MovementDirectionHorizontal - - - - - Float - - Float - - - - - - - MovementDirectionVertical + MaxBrightness - Float + Int - Float + Int - MovementSpeedPercent + MinBrightness @@ -22705,42 +22804,14 @@ This can increase performance, and also silently ignore critical errors - RandActivationF - - - - - Float - - Float - - - - - - - RandF - - - - - Float - - Float - - - - - - - RandInstanceF + PulseSpeedPercent - Float + Int - Float + Int @@ -22788,76 +22859,6 @@ This can increase performance, and also silently ignore critical errors - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ViewProj - - - - - System.Single[][] - - System.Single[][] - - - - - @@ -22880,11 +22881,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEmbossColorShader - OBSEmbossColorShader + Get-OBSDynamicMaskShader + OBSDynamicMaskShader Get - Get-OBSEmbossColorShader [[-AngleSteps] <int>] [[-RadiusSteps] <int>] [[-AmpFactor] <float>] [[-UpDownPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSDynamicMaskShader [[-InputSource] <string>] [[-RedBaseValue] <float>] [[-RedRedInputValue] <float>] [[-RedGreenInputValue] <float>] [[-RedBlueInputValue] <float>] [[-RedAlphaInputValue] <float>] [[-RedMultiplier] <float>] [[-GreenBaseValue] <float>] [[-GreenRedInputValue] <float>] [[-GreenGreenInputValue] <float>] [[-GreenBlueInputValue] <float>] [[-GreenAlphaInputValue] <float>] [[-GreenMultiplier] <float>] [[-BlueBaseValue] <float>] [[-BlueRedInputValue] <float>] [[-BlueGreenInputValue] <float>] [[-BlueBlueInputValue] <float>] [[-BlueAlphaInputValue] <float>] [[-BlueMultiplier] <float>] [[-AlphaBaseValue] <float>] [[-AlphaRedInputValue] <float>] [[-AlphaGreenInputValue] <float>] [[-AlphaBlueInputValue] <float>] [[-AlphaAlphaInputValue] <float>] [[-AlphaMultiplier] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -22892,37 +22893,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSEmbossColorShader + Get-OBSDynamicMaskShader - AngleSteps + InputSource - Int + String - Int + String - RadiusSteps + RedBaseValue - Int + Float - Int + Float - AmpFactor + RedRedInputValue @@ -22936,657 +22937,175 @@ This can increase performance, and also silently ignore critical errors - UpDownPercent + RedGreenInputValue - Int + Float - Int + Float - - ApplyToAlphaLayer + + RedBlueInputValue - Switch + Float - Switch + Float - - Notes + + RedAlphaInputValue - String + Float - String + Float - - SourceName + + RedMultiplier - String + Float - String + Float - - FilterName + + GreenBaseValue - String + Float - String + Float - - ShaderText + + GreenRedInputValue - String + Float - String + Float - - Force + + GreenGreenInputValue - Switch + Float - Switch + Float - - PassThru + + GreenBlueInputValue - Switch + Float - Switch + Float - - NoResponse + + GreenAlphaInputValue - Switch + Float - Switch + Float - - UseShaderTime + + GreenMultiplier - Switch + Float - Switch + Float - - - - - AmpFactor - - - - - Float - - Float - - - - - - - AngleSteps - - - - - Int - - Int - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - Notes - - - - - String - - String - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - RadiusSteps - - - - - Int - - Int - - - - - - - ShaderText - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - UpDownPercent - - - - - Int - - Int - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - - - System.String - - - - - - - - - System.Object - - - - - - - - - Get-OBSEmbossShader - OBSEmbossShader - Get - - Get-OBSEmbossShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-UseColor] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSEmbossShader - - UseColor - - - - - Switch - - Switch - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse + + BlueBaseValue - Switch + Float - Switch + Float - - UseShaderTime + + BlueRedInputValue - Switch + Float - Switch + Float - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - ShaderText - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - UseColor - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - - - System.String - - - - - - - - - System.Object - - - - - - - - - Get-OBSExeldroBentCameraShader - OBSExeldroBentCameraShader - Get - - Get-OBSExeldroBentCameraShader [[-LeftSideWidth] <float>] [[-LeftSideSize] <float>] [[-LeftSideShadow] <float>] [[-LeftFlipWidth] <float>] [[-LeftFlipShadow] <float>] [[-RightSideWidth] <float>] [[-RightSideSize] <float>] [[-RightSideShadow] <float>] [[-RightFlipWidth] <float>] [[-RightFlipShadow] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSExeldroBentCameraShader - - LeftSideWidth + + BlueGreenInputValue @@ -23599,8 +23118,8 @@ This can increase performance, and also silently ignore critical errors - - LeftSideSize + + BlueBlueInputValue @@ -23613,8 +23132,8 @@ This can increase performance, and also silently ignore critical errors - - LeftSideShadow + + BlueAlphaInputValue @@ -23627,8 +23146,8 @@ This can increase performance, and also silently ignore critical errors - - LeftFlipWidth + + BlueMultiplier @@ -23641,8 +23160,8 @@ This can increase performance, and also silently ignore critical errors - - LeftFlipShadow + + AlphaBaseValue @@ -23655,8 +23174,8 @@ This can increase performance, and also silently ignore critical errors - - RightSideWidth + + AlphaRedInputValue @@ -23669,8 +23188,8 @@ This can increase performance, and also silently ignore critical errors - - RightSideSize + + AlphaGreenInputValue @@ -23683,8 +23202,8 @@ This can increase performance, and also silently ignore critical errors - - RightSideShadow + + AlphaBlueInputValue @@ -23697,8 +23216,8 @@ This can increase performance, and also silently ignore critical errors - - RightFlipWidth + + AlphaAlphaInputValue @@ -23711,8 +23230,8 @@ This can increase performance, and also silently ignore critical errors - - RightFlipShadow + + AlphaMultiplier @@ -23725,7 +23244,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -23739,7 +23258,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -23753,7 +23272,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -23827,35 +23346,35 @@ This can increase performance, and also silently ignore critical errors - FilterName + AlphaAlphaInputValue - String + Float - String + Float - Force + AlphaBaseValue - Switch + Float - Switch + Float - LeftFlipShadow + AlphaBlueInputValue @@ -23869,7 +23388,7 @@ This can increase performance, and also silently ignore critical errors - LeftFlipWidth + AlphaGreenInputValue @@ -23883,7 +23402,7 @@ This can increase performance, and also silently ignore critical errors - LeftSideShadow + AlphaMultiplier @@ -23897,7 +23416,7 @@ This can increase performance, and also silently ignore critical errors - LeftSideSize + AlphaRedInputValue @@ -23911,7 +23430,7 @@ This can increase performance, and also silently ignore critical errors - LeftSideWidth + BlueAlphaInputValue @@ -23925,35 +23444,35 @@ This can increase performance, and also silently ignore critical errors - NoResponse + BlueBaseValue - Switch + Float - Switch + Float - PassThru + BlueBlueInputValue - Switch + Float - Switch + Float - RightFlipShadow + BlueGreenInputValue @@ -23967,7 +23486,7 @@ This can increase performance, and also silently ignore critical errors - RightFlipWidth + BlueMultiplier @@ -23981,7 +23500,7 @@ This can increase performance, and also silently ignore critical errors - RightSideShadow + BlueRedInputValue @@ -23995,7 +23514,35 @@ This can increase performance, and also silently ignore critical errors - RightSideSize + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + GreenAlphaInputValue @@ -24009,7 +23556,7 @@ This can increase performance, and also silently ignore critical errors - RightSideWidth + GreenBaseValue @@ -24023,241 +23570,77 @@ This can increase performance, and also silently ignore critical errors - ShaderText + GreenBlueInputValue - String + Float - String + Float - SourceName + GreenGreenInputValue - String + Float - String + Float - UseShaderTime + GreenMultiplier - Switch + Float - Switch + Float - - - + + GreenRedInputValue + + + + + Float - System.String + Float - - - - - + + + + + InputSource + + + + + String - System.Object + String - - - - - - - Get-OBSFadeTransitionShader - OBSFadeTransitionShader - Get - - Get-OBSFadeTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSFadeTransitionShader - - ImageA - - - - - String - - String - - - - - - - ImageB - - - - - String - - String - - - - - - - TransitionTime - - - - - Float - - Float - - - - - - - ConvertLinear - - - - - Switch - - Switch - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - + + + - ConvertLinear + NoResponse @@ -24271,105 +23654,105 @@ This can increase performance, and also silently ignore critical errors - FilterName + PassThru - String + Switch - String + Switch - Force + RedAlphaInputValue - Switch + Float - Switch + Float - ImageA + RedBaseValue - String + Float - String + Float - ImageB + RedBlueInputValue - String + Float - String + Float - NoResponse + RedGreenInputValue - Switch + Float - Switch + Float - PassThru + RedMultiplier - Switch + Float - Switch + Float - ShaderText + RedRedInputValue - String + Float - String + Float - SourceName + ShaderText @@ -24383,14 +23766,14 @@ This can increase performance, and also silently ignore critical errors - TransitionTime + SourceName - Float + String - Float + String @@ -24432,11 +23815,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorGradientShader - OBSFillColorGradientShader + Get-OBSEdgeDetectionShader + OBSEdgeDetectionShader Get - Get-OBSFillColorGradientShader [[-Fill] <float>] [[-GradientWidth] <float>] [[-GradientOffset] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSEdgeDetectionShader [[-Sensitivity] <float>] [[-EdgeColor] <string>] [[-NonEdgeColor] <string>] [[-AlphaLevel] <float>] [[-RandF] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertEdge] [-EdgeMultiply] [-NonEdgeMultiply] [-AlphaChannel] [-AlphaInvert] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -24444,9 +23827,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorGradientShader + Get-OBSEdgeDetectionShader - Fill + Sensitivity @@ -24459,22 +23842,92 @@ This can increase performance, and also silently ignore critical errors + + InvertEdge + + + + + Switch + + Switch + + + + + - GradientWidth + EdgeColor - Float + String - Float + String + + + + + + + EdgeMultiply + + + + + Switch + + Switch - GradientOffset + NonEdgeColor + + + + + String + + String + + + + + + + NonEdgeMultiply + + + + + Switch + + Switch + + + + + + + AlphaChannel + + + + + Switch + + Switch + + + + + + + AlphaLevel @@ -24487,22 +23940,36 @@ This can increase performance, and also silently ignore critical errors - - FillDirection + + AlphaInvert - Int + Switch - Int + Switch - FillColor + RandF + + + + + Float + + Float + + + + + + + Notes @@ -24515,7 +23982,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -24529,7 +23996,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -24543,7 +24010,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -24617,49 +24084,49 @@ This can increase performance, and also silently ignore critical errors - Fill + AlphaChannel - Float + Switch - Float + Switch - FillColor + AlphaInvert - String + Switch - String + Switch - FillDirection + AlphaLevel - Int + Float - Int + Float - FilterName + EdgeColor @@ -24673,7 +24140,7 @@ This can increase performance, and also silently ignore critical errors - Force + EdgeMultiply @@ -24687,35 +24154,35 @@ This can increase performance, and also silently ignore critical errors - GradientOffset + FilterName - Float + String - Float + String - GradientWidth + Force - Float + Switch - Float + Switch - NoResponse + InvertEdge @@ -24729,7 +24196,21 @@ This can increase performance, and also silently ignore critical errors - PassThru + NonEdgeColor + + + + + String + + String + + + + + + + NonEdgeMultiply @@ -24743,21 +24224,21 @@ This can increase performance, and also silently ignore critical errors - ShaderText + NoResponse - String + Switch - String + Switch - SourceName + Notes @@ -24771,7 +24252,7 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + PassThru @@ -24784,9 +24265,79 @@ This can increase performance, and also silently ignore critical errors - - - + + RandF + + + + + Float + + Float + + + + + + + Sensitivity + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + System.String @@ -24806,21 +24357,114 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorLinearShader - OBSFillColorLinearShader + Get-OBSEffect + OBSEffect Get - Get-OBSFillColorLinearShader [[-Fill] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Gets OBS Effects 0.2.0.1 + Gets effects currently loaded into OBS-PowerShell. + An effect can be thought of as a name with a series of messages to OBS. + Those messages can be defined in a .json file or a script, in any module that tags OBS. + They can also be defined in a function or script named like: + * `*.OBS.FX.*` + * `*.OBS.Effect.*` + * `*.OBS.Effects.*` - Get-OBSFillColorLinearShader + Get-OBSEffect + + Name + + The name of the effect. + + String + + String + + + + + + + + + + Name + + The name of the effect. + + String + + String + + + + + + + + + Import-OBSEffect + + + + + Remove-OBSEffect + + + + + + + + Get-OBSEmbersShader + OBSEmbersShader + Get + + Get-OBSEmbersShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-AnimationSpeed] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-MovementSpeedPercent] <int>] [[-LayersCount] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSEmbersShader - Fill + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + Image + + + + + String + + String + + + + + + + ElapsedTime @@ -24833,8 +24477,106 @@ This can increase performance, and also silently ignore critical errors - - FillDirection + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + RandF + + + + + Float + + Float + + + + + + + RandInstanceF + + + + + Float + + Float + + + + + + + RandActivationF + + + + + Float + + Float + + + + + + + Loops @@ -24847,8 +24589,22 @@ This can increase performance, and also silently ignore critical errors - - FillColor + + LocalTime + + + + + Float + + Float + + + + + + + Notes @@ -24861,7 +24617,133 @@ This can increase performance, and also silently ignore critical errors - + + AnimationSpeed + + + + + Float + + Float + + + + + + + MovementDirectionHorizontal + + + + + Float + + Float + + + + + + + MovementDirectionVertical + + + + + Float + + Float + + + + + + + MovementSpeedPercent + + + + + Int + + Int + + + + + + + LayersCount + + + + + Int + + Int + + + + + + + LumaMin + + + + + Float + + Float + + + + + + + LumaMinSmooth + + + + + Float + + Float + + + + + + + AlphaPercentage + + + + + Float + + Float + + + + + + + ApplyToAlphaLayer + + + + + Switch + + Switch + + + + + + SourceName @@ -24875,7 +24757,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -24889,7 +24771,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -24963,7 +24845,7 @@ This can increase performance, and also silently ignore critical errors - Fill + AlphaPercentage @@ -24977,28 +24859,42 @@ This can increase performance, and also silently ignore critical errors - FillColor + AnimationSpeed - String + Float - String + Float - FillDirection + ApplyToAlphaLayer - Int + Switch - Int + Switch + + + + + + + ElapsedTime + + + + + Float + + Float @@ -25033,102 +24929,354 @@ This can increase performance, and also silently ignore critical errors - NoResponse + Image - Switch + String - Switch + String - PassThru + LayersCount - Switch + Int - Switch + Int - ShaderText + LocalTime - String + Float - String + Float - SourceName + Loops - String + Int - String + Int - UseShaderTime + LumaMin - Switch + Float - Switch + Float - - - - - System.String - - - - - - - + + LumaMinSmooth + + + + + Float - System.Object + Float - - - - - + + + + + MovementDirectionHorizontal + + + + + Float + + Float + + + + + + + MovementDirectionVertical + + + + + Float + + Float + + + + + + + MovementSpeedPercent + + + + + Int + + Int + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + RandActivationF + + + + + Float + + Float + + + + + + + RandF + + + + + Float + + Float + + + + + + + RandInstanceF + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + - Get-OBSFillColorRadialDegreesShader - OBSFillColorRadialDegreesShader + Get-OBSEmbossColorShader + OBSEmbossColorShader Get - Get-OBSFillColorRadialDegreesShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSEmbossColorShader [[-AngleSteps] <int>] [[-RadiusSteps] <int>] [[-AmpFactor] <float>] [[-UpDownPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -25136,9 +25284,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorRadialDegreesShader + Get-OBSEmbossColorShader - FillDirection + AngleSteps @@ -25152,21 +25300,21 @@ This can increase performance, and also silently ignore critical errors - Fill + RadiusSteps - Float + Int - Float + Int - StartAngle + AmpFactor @@ -25180,35 +25328,35 @@ This can increase performance, and also silently ignore critical errors - OffsetX + UpDownPercent - Float + Int - Float + Int - - OffsetY + + ApplyToAlphaLayer - Float + Switch - Float + Switch - - FillColor + + Notes @@ -25221,7 +25369,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -25235,7 +25383,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -25249,7 +25397,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -25323,7 +25471,7 @@ This can increase performance, and also silently ignore critical errors - Fill + AmpFactor @@ -25337,28 +25485,28 @@ This can increase performance, and also silently ignore critical errors - FillColor + AngleSteps - String + Int - String + Int - FillDirection + ApplyToAlphaLayer - Int + Switch - Int + Switch @@ -25407,42 +25555,42 @@ This can increase performance, and also silently ignore critical errors - OffsetX + Notes - Float + String - Float + String - OffsetY + PassThru - Float + Switch - Float + Switch - PassThru + RadiusSteps - Switch + Int - Switch + Int @@ -25477,14 +25625,14 @@ This can increase performance, and also silently ignore critical errors - StartAngle + UpDownPercent - Float + Int - Float + Int @@ -25526,11 +25674,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorRadialPercentageShader - OBSFillColorRadialPercentageShader + Get-OBSEmbossShader + OBSEmbossShader Get - Get-OBSFillColorRadialPercentageShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSEmbossShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-UseColor] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -25538,92 +25686,36 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFillColorRadialPercentageShader - - FillDirection - - - - - Int - - Int - - - - - - - Fill - - - - - Float - - Float - - - - - - - StartAngle - - - - - Float - - Float - - - - - - - OffsetX - - - - - Float - - Float - - - - - - - OffsetY + Get-OBSEmbossShader + + UseColor - Float + Switch - Float + Switch - - FillColor + + ApplyToAlphaLayer - String + Switch - String + Switch - + SourceName @@ -25637,7 +25729,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -25651,7 +25743,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -25725,42 +25817,14 @@ This can increase performance, and also silently ignore critical errors - Fill - - - - - Float - - Float - - - - - - - FillColor - - - - - String - - String - - - - - - - FillDirection + ApplyToAlphaLayer - Int + Switch - Int + Switch @@ -25808,34 +25872,6 @@ This can increase performance, and also silently ignore critical errors - - OffsetX - - - - - Float - - Float - - - - - - - OffsetY - - - - - Float - - Float - - - - - PassThru @@ -25879,14 +25915,14 @@ This can increase performance, and also silently ignore critical errors - StartAngle + UseColor - Float + Switch - Float + Switch @@ -25928,11 +25964,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFilterTemplateShader - OBSFilterTemplateShader + Get-OBSExeldroBentCameraShader + OBSExeldroBentCameraShader Get - Get-OBSFilterTemplateShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSExeldroBentCameraShader [[-LeftSideWidth] <float>] [[-LeftSideSize] <float>] [[-LeftSideShadow] <float>] [[-LeftFlipWidth] <float>] [[-LeftFlipShadow] <float>] [[-RightSideWidth] <float>] [[-RightSideSize] <float>] [[-RightSideShadow] <float>] [[-RightFlipWidth] <float>] [[-RightFlipShadow] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -25940,37 +25976,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFilterTemplateShader + Get-OBSExeldroBentCameraShader - ViewProj + LeftSideWidth - System.Single[][] + Float - System.Single[][] + Float - Image + LeftSideSize - String + Float - String + Float - ElapsedTime + LeftSideShadow @@ -25984,63 +26020,63 @@ This can increase performance, and also silently ignore critical errors - UvOffset + LeftFlipWidth - System.Single[] + Float - System.Single[] + Float - UvScale + LeftFlipShadow - System.Single[] + Float - System.Single[] + Float - UvPixelInterval + RightSideWidth - System.Single[] + Float - System.Single[] + Float - UvSize + RightSideSize - System.Single[] + Float - System.Single[] + Float - RandF + RightSideShadow @@ -26054,7 +26090,7 @@ This can increase performance, and also silently ignore critical errors - RandInstanceF + RightFlipWidth @@ -26068,35 +26104,7 @@ This can increase performance, and also silently ignore critical errors - RandActivationF - - - - - Float - - Float - - - - - - - Loops - - - - - Int - - Int - - - - - - - LocalTime + RightFlipShadow @@ -26109,21 +26117,7 @@ This can increase performance, and also silently ignore critical errors - - Notes - - - - - String - - String - - - - - - + SourceName @@ -26137,7 +26131,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -26151,7 +26145,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -26224,20 +26218,6 @@ This can increase performance, and also silently ignore critical errors - - ElapsedTime - - - - - Float - - Float - - - - - FilterName @@ -26267,21 +26247,21 @@ This can increase performance, and also silently ignore critical errors - Image + LeftFlipShadow - String + Float - String + Float - LocalTime + LeftFlipWidth @@ -26295,49 +26275,49 @@ This can increase performance, and also silently ignore critical errors - Loops + LeftSideShadow - Int + Float - Int + Float - NoResponse + LeftSideSize - Switch + Float - Switch + Float - Notes + LeftSideWidth - String + Float - String + Float - PassThru + NoResponse @@ -26351,21 +26331,21 @@ This can increase performance, and also silently ignore critical errors - RandActivationF + PassThru - Float + Switch - Float + Switch - RandF + RightFlipShadow @@ -26379,7 +26359,7 @@ This can increase performance, and also silently ignore critical errors - RandInstanceF + RightFlipWidth @@ -26393,112 +26373,84 @@ This can increase performance, and also silently ignore critical errors - ShaderText - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - UseShaderTime + RightSideShadow - Switch + Float - Switch + Float - UvOffset + RightSideSize - System.Single[] + Float - System.Single[] + Float - UvPixelInterval + RightSideWidth - System.Single[] + Float - System.Single[] + Float - UvScale + ShaderText - System.Single[] + String - System.Single[] + String - UvSize + SourceName - System.Single[] + String - System.Single[] + String - ViewProj + UseShaderTime - System.Single[][] + Switch - System.Single[][] + Switch @@ -26526,11 +26478,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFire3Shader - OBSFire3Shader + Get-OBSFadeTransitionShader + OBSFadeTransitionShader Get - Get-OBSFire3Shader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-FlameSize] <float>] [[-SparkGridHeight] <float>] [[-FlameModifier] <float>] [[-FlameTongueSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-FullWidth] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFadeTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -26538,23 +26490,23 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFire3Shader + Get-OBSFadeTransitionShader - ViewProj + ImageA - System.Single[][] + String - System.Single[][] + String - Image + ImageB @@ -26568,7 +26520,7 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + TransitionTime @@ -26581,120 +26533,312 @@ This can increase performance, and also silently ignore critical errors - - UvOffset + + ConvertLinear - System.Single[] + Switch - System.Single[] + Switch - - UvScale + + SourceName - System.Single[] + String - System.Single[] + String - - UvPixelInterval + + FilterName - System.Single[] + String - System.Single[] + String - - UvSize + + ShaderText - System.Single[] + String - System.Single[] + String - - RandF + + Force - Float + Switch - Float + Switch - - RandInstanceF + + PassThru - Float + Switch - Float + Switch - - RandActivationF + + NoResponse - Float + Switch - Float + Switch - - Loops + + UseShaderTime - Int + Switch - Int + Switch - - LocalTime + + + + + ConvertLinear + + + + + Switch + + Switch + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + ImageA + + + + + String + + String + + + + + + + ImageB + + + + + String + + String + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + TransitionTime + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSFillColorGradientShader + OBSFillColorGradientShader + Get + + Get-OBSFillColorGradientShader [[-Fill] <float>] [[-GradientWidth] <float>] [[-GradientOffset] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSFillColorGradientShader + + Fill @@ -26707,8 +26851,8 @@ This can increase performance, and also silently ignore critical errors - - MovementDirectionHorizontal + + GradientWidth @@ -26721,8 +26865,8 @@ This can increase performance, and also silently ignore critical errors - - MovementDirectionVertical + + GradientOffset @@ -26735,8 +26879,8 @@ This can increase performance, and also silently ignore critical errors - - AlphaPercentage + + FillDirection @@ -26749,209 +26893,41 @@ This can increase performance, and also silently ignore critical errors - - Speed + + FillColor - Int + String - Int + String - - Invert + + SourceName - Switch + String - Switch + String - - LumaMin + + FilterName - Float - - Float - - - - - - - LumaMinSmooth - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ReplaceImageColor - - - - - Switch - - Switch - - - - - - - ColorToReplace - - - - - String - - String - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - FullWidth - - - - - Switch - - Switch - - - - - - - FlameSize - - - - - Float - - Float - - - - - - - SparkGridHeight - - - - - Float - - Float - - - - - - - FlameModifier - - - - - Float - - Float - - - - - - - FlameTongueSize - - - - - Float - - Float - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String + String String @@ -26959,7 +26935,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -27033,49 +27009,21 @@ This can increase performance, and also silently ignore critical errors - AlphaPercentage - - - - - Int - - Int - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor + Fill - Switch + Float - Switch + Float - ColorToReplace + FillColor @@ -27089,14 +27037,14 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + FillDirection - Float + Int - Float + Int @@ -27117,21 +27065,21 @@ This can increase performance, and also silently ignore critical errors - FlameModifier + Force - Float + Switch - Float + Switch - FlameSize + GradientOffset @@ -27145,7 +27093,7 @@ This can increase performance, and also silently ignore critical errors - FlameTongueSize + GradientWidth @@ -27159,7 +27107,7 @@ This can increase performance, and also silently ignore critical errors - Force + NoResponse @@ -27173,7 +27121,7 @@ This can increase performance, and also silently ignore critical errors - FullWidth + PassThru @@ -27187,7 +27135,7 @@ This can increase performance, and also silently ignore critical errors - Image + ShaderText @@ -27201,63 +27149,213 @@ This can increase performance, and also silently ignore critical errors - Invert + SourceName - Switch + String - Switch + String - LocalTime + UseShaderTime - Float + Switch - Float + Switch - - Loops - - - - - Int + + + - Int + System.String + - - - - - LumaMin - - - - - Float + + + + - Float + System.Object + - - - + + + + + + Get-OBSFillColorLinearShader + OBSFillColorLinearShader + Get + + Get-OBSFillColorLinearShader [[-Fill] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSFillColorLinearShader + + Fill + + + + + Float + + Float + + + + + + + FillDirection + + + + + Int + + Int + + + + + + + FillColor + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + - LumaMinSmooth + Fill @@ -27271,49 +27369,49 @@ This can increase performance, and also silently ignore critical errors - MovementDirectionHorizontal + FillColor - Float + String - Float + String - MovementDirectionVertical + FillDirection - Float + Int - Float + Int - NoResponse + FilterName - Switch + String - Switch + String - PassThru + Force @@ -27327,49 +27425,21 @@ This can increase performance, and also silently ignore critical errors - RandActivationF - - - - - Float - - Float - - - - - - - RandF - - - - - Float - - Float - - - - - - - RandInstanceF + NoResponse - Float + Switch - Float + Switch - ReplaceImageColor + PassThru @@ -27411,124 +27481,26 @@ This can increase performance, and also silently ignore critical errors - SparkGridHeight + UseShaderTime - Float + Switch - Float + Switch - - Speed - - - - - Int + + + - Int + System.String - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ViewProj - - - - - System.Single[][] - - System.Single[][] - - - - - - - - - - System.String - - + @@ -27544,11 +27516,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireShader - OBSFireShader + Get-OBSFillColorRadialDegreesShader + OBSFillColorRadialDegreesShader Get - Get-OBSFireShader [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-FlameSize] <int>] [[-FireType] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFillColorRadialDegreesShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -27556,9 +27528,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireShader + Get-OBSFillColorRadialDegreesShader - AlphaPercentage + FillDirection @@ -27572,63 +27544,35 @@ This can increase performance, and also silently ignore critical errors - Speed + Fill - Int + Float - Int + Float - FlameSize + StartAngle - Int + Float - Int + Float - FireType - - - - - Int - - Int - - - - - - - Invert - - - - - Switch - - Switch - - - - - - - LumaMin + OffsetX @@ -27641,8 +27585,8 @@ This can increase performance, and also silently ignore critical errors - - LumaMinSmooth + + OffsetY @@ -27655,64 +27599,8 @@ This can increase performance, and also silently ignore critical errors - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ReplaceImageColor - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorToReplace - - - - - String - - String - - - - - - - Notes + + FillColor @@ -27725,7 +27613,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -27739,7 +27627,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -27753,7 +27641,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -27827,63 +27715,21 @@ This can increase performance, and also silently ignore critical errors - AlphaPercentage - - - - - Int - - Int - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorToReplace + Fill - String + Float - String + Float - FilterName + FillColor @@ -27897,7 +27743,7 @@ This can increase performance, and also silently ignore critical errors - FireType + FillDirection @@ -27911,14 +27757,14 @@ This can increase performance, and also silently ignore critical errors - FlameSize + FilterName - Int + String - Int + String @@ -27939,7 +27785,7 @@ This can increase performance, and also silently ignore critical errors - Invert + NoResponse @@ -27953,7 +27799,7 @@ This can increase performance, and also silently ignore critical errors - LumaMin + OffsetX @@ -27967,7 +27813,7 @@ This can increase performance, and also silently ignore critical errors - LumaMinSmooth + OffsetY @@ -27980,34 +27826,6 @@ This can increase performance, and also silently ignore critical errors - - NoResponse - - - - - Switch - - Switch - - - - - - - Notes - - - - - String - - String - - - - - PassThru @@ -28022,20 +27840,6 @@ This can increase performance, and also silently ignore critical errors - - ReplaceImageColor - - - - - Switch - - Switch - - - - - ShaderText @@ -28065,14 +27869,14 @@ This can increase performance, and also silently ignore critical errors - Speed + StartAngle - Int + Float - Int + Float @@ -28114,11 +27918,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireworks2Shader - OBSFireworks2Shader + Get-OBSFillColorRadialPercentageShader + OBSFillColorRadialPercentageShader Get - Get-OBSFireworks2Shader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFillColorRadialPercentageShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -28126,9 +27930,23 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireworks2Shader + Get-OBSFillColorRadialPercentageShader - Speed + FillDirection + + + + + Int + + Int + + + + + + + Fill @@ -28141,7 +27959,63 @@ This can increase performance, and also silently ignore critical errors - + + StartAngle + + + + + Float + + Float + + + + + + + OffsetX + + + + + Float + + Float + + + + + + + OffsetY + + + + + Float + + Float + + + + + + + FillColor + + + + + String + + String + + + + + + SourceName @@ -28155,7 +28029,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -28169,7 +28043,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -28242,6 +28116,48 @@ This can increase performance, and also silently ignore critical errors + + Fill + + + + + Float + + Float + + + + + + + FillColor + + + + + String + + String + + + + + + + FillDirection + + + + + Int + + Int + + + + + FilterName @@ -28284,6 +28200,34 @@ This can increase performance, and also silently ignore critical errors + + OffsetX + + + + + Float + + Float + + + + + + + OffsetY + + + + + Float + + Float + + + + + PassThru @@ -28327,7 +28271,7 @@ This can increase performance, and also silently ignore critical errors - Speed + StartAngle @@ -28376,11 +28320,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireworksShader - OBSFireworksShader + Get-OBSFilterTemplateShader + OBSFilterTemplateShader Get - Get-OBSFireworksShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowFlash] [-ShowStars] [-UseTransparancy] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFilterTemplateShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -28388,50 +28332,190 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFireworksShader - - ShowFlash + Get-OBSFilterTemplateShader + + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] - - ShowStars + + Image - Switch + String - Switch + String - - UseTransparancy + + ElapsedTime - Switch + Float - Switch + Float - + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + RandF + + + + + Float + + Float + + + + + + + RandInstanceF + + + + + Float + + Float + + + + + + + RandActivationF + + + + + Float + + Float + + + + + + + Loops + + + + + Int + + Int + + + + + + + LocalTime + + + + + Float + + Float + + + + + + + Notes + + + + + String + + String + + + + + + SourceName @@ -28445,7 +28529,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -28459,7 +28543,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -28533,35 +28617,35 @@ This can increase performance, and also silently ignore critical errors - FilterName + ElapsedTime - String + Float - String + Float - Force + FilterName - Switch + String - Switch + String - NoResponse + Force @@ -28575,49 +28659,49 @@ This can increase performance, and also silently ignore critical errors - PassThru + Image - Switch + String - Switch + String - ShaderText + LocalTime - String + Float - String + Float - ShowFlash + Loops - Switch + Int - Switch + Int - ShowStars + NoResponse @@ -28631,7 +28715,7 @@ This can increase performance, and also silently ignore critical errors - SourceName + Notes @@ -28645,7 +28729,7 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + PassThru @@ -28659,199 +28743,21 @@ This can increase performance, and also silently ignore critical errors - UseTransparancy + RandActivationF - Switch + Float - Switch + Float - - - - - System.String - - - - - - - - - System.Object - - - - - - - - - Get-OBSFisheyeShader - OBSFisheyeShader - Get - - Get-OBSFisheyeShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-Power] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSFisheyeShader - - CenterXPercent - - - - - Float - - Float - - - - - - - CenterYPercent - - - - - Float - - Float - - - - - - - Power - - - - - Float - - Float - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - - CenterXPercent + RandF @@ -28865,7 +28771,7 @@ This can increase performance, and also silently ignore critical errors - CenterYPercent + RandInstanceF @@ -28879,7 +28785,7 @@ This can increase performance, and also silently ignore critical errors - FilterName + ShaderText @@ -28893,21 +28799,21 @@ This can increase performance, and also silently ignore critical errors - Force + SourceName - Switch + String - Switch + String - NoResponse + UseShaderTime @@ -28921,70 +28827,70 @@ This can increase performance, and also silently ignore critical errors - PassThru + UvOffset - Switch + System.Single[] - Switch + System.Single[] - Power + UvPixelInterval - Float + System.Single[] - Float + System.Single[] - ShaderText + UvScale - String + System.Single[] - String + System.Single[] - SourceName + UvSize - String + System.Single[] - String + System.Single[] - UseShaderTime + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] @@ -29012,11 +28918,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFisheyeXyShader - OBSFisheyeXyShader + Get-OBSFire3Shader + OBSFire3Shader Get - Get-OBSFisheyeXyShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-PowerX] <float>] [[-PowerY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFire3Shader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-FlameSize] <float>] [[-SparkGridHeight] <float>] [[-FlameModifier] <float>] [[-FlameTongueSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-FullWidth] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -29024,37 +28930,37 @@ This can increase performance, and also silently ignore critical errors - Get-OBSFisheyeXyShader + Get-OBSFire3Shader - CenterXPercent + ViewProj - Float + System.Single[][] - Float + System.Single[][] - CenterYPercent + Image - Float + String - Float + String - PowerX + ElapsedTime @@ -29068,381 +28974,189 @@ This can increase performance, and also silently ignore critical errors - PowerY + UvOffset - Float + System.Single[] - Float + System.Single[] - - SourceName + + UvScale - String + System.Single[] - String + System.Single[] - - FilterName + + UvPixelInterval - String + System.Single[] - String + System.Single[] - ShaderText + UvSize - String + System.Single[] - String + System.Single[] - - Force + + RandF - Switch + Float - Switch + Float - - PassThru + + RandInstanceF - Switch + Float - Switch + Float - - NoResponse + + RandActivationF - Switch + Float - Switch + Float - - UseShaderTime + + Loops - Switch + Int - Switch + Int - - - - - CenterXPercent - - - - - Float - - Float - - - - - - - CenterYPercent - - - - - Float - - Float - - - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - PowerX - - - - - Float - - Float - - - - - - - PowerY - - - - - Float - - Float - - - - - - - ShaderText - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - - - System.String - - - - - - - - - System.Object - - - - - - - - - Get-OBSFlipShader - OBSFlipShader - Get - - Get-OBSFlipShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSFlipShader - - Horizontal + + LocalTime - Switch + Float - Switch + Float - - Vertical + + MovementDirectionHorizontal - Switch + Float - Switch + Float - - SourceName + + MovementDirectionVertical - String + Float - String + Float - - FilterName + + AlphaPercentage - String + Int - String + Int - - ShaderText + + Speed - String + Int - String + Int - Force + Invert @@ -29455,36 +29169,36 @@ This can increase performance, and also silently ignore critical errors - - PassThru + + LumaMin - Switch + Float - Switch + Float - - NoResponse + + LumaMinSmooth - Switch + Float - Switch + Float - UseShaderTime + ApplyToImage @@ -29497,214 +29211,50 @@ This can increase performance, and also silently ignore critical errors - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - Horizontal - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - ShaderText - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - Vertical - - - - - Switch - - Switch - - - - - - - - - - System.String - - - - - - - - - System.Object - - - - - - - - - Get-OBSFrostedGlassShader - OBSFrostedGlassShader - Get - - Get-OBSFrostedGlassShader [[-AlphaPercent] <float>] [[-Amount] <float>] [[-Scale] <float>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animate] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSFrostedGlassShader - - AlphaPercent + + ReplaceImageColor - Float + Switch - Float + Switch - - Amount + + ColorToReplace - Float + String - Float + String - - Scale + + ApplyToSpecificColor - Float + Switch - Float + Switch - Animate + FullWidth @@ -29717,22 +29267,22 @@ This can increase performance, and also silently ignore critical errors - - HorizontalBorder + + FlameSize - Switch + Float - Switch + Float - - BorderOffset + + SparkGridHeight @@ -29745,35 +29295,35 @@ This can increase performance, and also silently ignore critical errors - - BorderColor + + FlameModifier - String + Float - String + Float - - Notes + + FlameTongueSize - String + Float - String + Float - + SourceName @@ -29787,7 +29337,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -29801,7 +29351,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -29875,35 +29425,35 @@ This can increase performance, and also silently ignore critical errors - AlphaPercent + AlphaPercentage - Float + Int - Float + Int - Amount + ApplyToImage - Float + Switch - Float + Switch - Animate + ApplyToSpecificColor @@ -29917,7 +29467,7 @@ This can increase performance, and also silently ignore critical errors - BorderColor + ColorToReplace @@ -29931,7 +29481,7 @@ This can increase performance, and also silently ignore critical errors - BorderOffset + ElapsedTime @@ -29959,63 +29509,63 @@ This can increase performance, and also silently ignore critical errors - Force + FlameModifier - Switch + Float - Switch + Float - HorizontalBorder + FlameSize - Switch + Float - Switch + Float - NoResponse + FlameTongueSize - Switch + Float - Switch + Float - Notes + Force - String + Switch - String + Switch - PassThru + FullWidth @@ -30029,255 +29579,91 @@ This can increase performance, and also silently ignore critical errors - Scale + Image - Float + String - Float + String - ShaderText + Invert - String + Switch - String + Switch - SourceName + LocalTime - String + Float - String + Float - UseShaderTime + Loops - Switch + Int - Switch + Int - - - + + LumaMin + + + + + Float - System.String + Float - - - - - + + + + + LumaMinSmooth + + + + + Float - System.Object + Float - - - - - - - Get-OBSGammaCorrectionShader - OBSGammaCorrectionShader - Get - - Get-OBSGammaCorrectionShader [[-Red] <float>] [[-Green] <float>] [[-Blue] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSGammaCorrectionShader - - Red - - - - - Float - - Float - - - - - - - Green - - - - - Float - - Float - - - - - - - Blue - - - - - Float - - Float - - - - - - - Notes - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - + + + - Blue + MovementDirectionHorizontal @@ -30291,21 +29677,21 @@ This can increase performance, and also silently ignore critical errors - FilterName + MovementDirectionVertical - String + Float - String + Float - Force + NoResponse @@ -30319,70 +29705,70 @@ This can increase performance, and also silently ignore critical errors - Green + PassThru - Float + Switch - Float + Switch - NoResponse + RandActivationF - Switch + Float - Switch + Float - Notes + RandF - String + Float - String + Float - PassThru + RandInstanceF - Switch + Float - Switch + Float - Red + ReplaceImageColor - Float + Switch - Float + Switch @@ -30416,6 +29802,34 @@ This can increase performance, and also silently ignore critical errors + + SparkGridHeight + + + + + Float + + Float + + + + + + + Speed + + + + + Int + + Int + + + + + UseShaderTime @@ -30430,6 +29844,76 @@ This can increase performance, and also silently ignore critical errors + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + @@ -30452,11 +29936,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurAdvancedShader - OBSGaussianBlurAdvancedShader + Get-OBSFireShader + OBSFireShader Get - Get-OBSGaussianBlurAdvancedShader [[-Directions] <float>] [[-Quality] <float>] [[-Size] <float>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFireShader [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-FlameSize] <int>] [[-FireType] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -30464,65 +29948,79 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurAdvancedShader + Get-OBSFireShader - Directions + AlphaPercentage - Float + Int - Float + Int - Quality + Speed - Float + Int - Float + Int - Size + FlameSize - Float + Int - Float + Int - MaskLeft + FireType - Float + Int - Float + Int + + + + + + + Invert + + + + + Switch + + Switch - MaskRight + LumaMin @@ -30536,7 +30034,7 @@ This can increase performance, and also silently ignore critical errors - MaskTop + LumaMinSmooth @@ -30549,22 +30047,64 @@ This can increase performance, and also silently ignore critical errors + + ApplyToImage + + + + + Switch + + Switch + + + + + + + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + - MaskBottom + ColorToReplace - Float + String - Float + String - - SourceName + + Notes @@ -30578,6 +30118,20 @@ This can increase performance, and also silently ignore critical errors + SourceName + + + + + String + + String + + + + + + FilterName @@ -30591,7 +30145,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -30665,35 +30219,35 @@ This can increase performance, and also silently ignore critical errors - Directions + AlphaPercentage - Float + Int - Float + Int - FilterName + ApplyToImage - String + Switch - String + Switch - Force + ApplyToSpecificColor @@ -30707,35 +30261,91 @@ This can increase performance, and also silently ignore critical errors - MaskBottom + ColorToReplace - Float + String - Float + String - MaskLeft + FilterName - Float + String - Float + String - MaskRight + FireType + + + + + Int + + Int + + + + + + + FlameSize + + + + + Int + + Int + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Invert + + + + + Switch + + Switch + + + + + + + LumaMin @@ -30749,7 +30359,7 @@ This can increase performance, and also silently ignore critical errors - MaskTop + LumaMinSmooth @@ -30776,6 +30386,20 @@ This can increase performance, and also silently ignore critical errors + + Notes + + + + + String + + String + + + + + PassThru @@ -30791,14 +30415,14 @@ This can increase performance, and also silently ignore critical errors - Quality + ReplaceImageColor - Float + Switch - Float + Switch @@ -30819,28 +30443,28 @@ This can increase performance, and also silently ignore critical errors - Size + SourceName - Float + String - Float + String - SourceName + Speed - String + Int - String + Int @@ -30882,11 +30506,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurShader - OBSGaussianBlurShader + Get-OBSFireworks2Shader + OBSFireworks2Shader Get - Get-OBSGaussianBlurShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ImageSize] <float[]>] [[-ImageTexel] <float[]>] [[-URadius] <int>] [[-UDiameter] <int>] [[-UTexelDelta] <float[]>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-Kernel] <string>] [[-KernelTexel] <float[]>] [[-PixelSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFireworks2Shader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -30894,23 +30518,23 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurShader + Get-OBSFireworks2Shader - ViewProj + Speed - System.Single[][] + Float - System.Single[][] + Float - - Image + + SourceName @@ -30923,175 +30547,283 @@ This can increase performance, and also silently ignore critical errors - - ImageSize + + FilterName - System.Single[] + String - System.Single[] + String - ImageTexel - - - - - System.Single[] - - System.Single[] - - - - - - - URadius - - - - - Int - - Int - - - - - - - UDiameter - - - - - Int - - Int - - - - - - - UTexelDelta + ShaderText - System.Single[] + String - System.Single[] + String - - ElapsedTime + + Force - Float + Switch - Float + Switch - - UvOffset + + PassThru - System.Single[] + Switch - System.Single[] + Switch - - UvScale + + NoResponse - System.Single[] + Switch - System.Single[] + Switch - - UvPixelInterval + + UseShaderTime - System.Single[] + Switch - System.Single[] + Switch - - Kernel + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Speed + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSFireworksShader + OBSFireworksShader + Get + + Get-OBSFireworksShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowFlash] [-ShowStars] [-UseTransparancy] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSFireworksShader + + ShowFlash - String + Switch - String + Switch - - KernelTexel + + ShowStars - System.Single[] + Switch - System.Single[] + Switch - - PixelSize + + UseTransparancy - Float + Switch - Float + Switch - + SourceName @@ -31105,7 +30837,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -31119,7 +30851,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -31192,20 +30924,6 @@ This can increase performance, and also silently ignore critical errors - - ElapsedTime - - - - - Float - - Float - - - - - FilterName @@ -31234,76 +30952,6 @@ This can increase performance, and also silently ignore critical errors - - Image - - - - - String - - String - - - - - - - ImageSize - - - - - System.Single[] - - System.Single[] - - - - - - - ImageTexel - - - - - System.Single[] - - System.Single[] - - - - - - - Kernel - - - - - String - - String - - - - - - - KernelTexel - - - - - System.Single[] - - System.Single[] - - - - - NoResponse @@ -31332,20 +30980,6 @@ This can increase performance, and also silently ignore critical errors - - PixelSize - - - - - Float - - Float - - - - - ShaderText @@ -31361,42 +30995,42 @@ This can increase performance, and also silently ignore critical errors - SourceName + ShowFlash - String + Switch - String + Switch - UDiameter + ShowStars - Int + Switch - Int + Switch - URadius + SourceName - Int + String - Int + String @@ -31417,82 +31051,26 @@ This can increase performance, and also silently ignore critical errors - UTexelDelta + UseTransparancy - System.Single[] + Switch - System.Single[] + Switch - - UvOffset - - - - - System.Single[] + + + - System.Single[] + System.String - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - ViewProj - - - - - System.Single[][] - - System.Single[][] - - - - - - - - - - System.String - - + @@ -31508,11 +31086,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurSimpleShader - OBSGaussianBlurSimpleShader + Get-OBSFisheyeShader + OBSFisheyeShader Get - Get-OBSGaussianBlurSimpleShader [[-Strength] <int>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFisheyeShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-Power] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -31520,37 +31098,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianBlurSimpleShader + Get-OBSFisheyeShader - Strength - - - - - Int - - Int - - - - - - - MaskLeft - - - - - Float - - Float - - - - - - - MaskRight + CenterXPercent @@ -31563,8 +31113,8 @@ This can increase performance, and also silently ignore critical errors - - MaskTop + + CenterYPercent @@ -31577,8 +31127,8 @@ This can increase performance, and also silently ignore critical errors - - MaskBottom + + Power @@ -31591,7 +31141,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -31605,7 +31155,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -31619,7 +31169,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -31693,35 +31243,7 @@ This can increase performance, and also silently ignore critical errors - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - MaskBottom + CenterXPercent @@ -31735,7 +31257,7 @@ This can increase performance, and also silently ignore critical errors - MaskLeft + CenterYPercent @@ -31749,28 +31271,28 @@ This can increase performance, and also silently ignore critical errors - MaskRight + FilterName - Float + String - Float + String - MaskTop + Force - Float + Switch - Float + Switch @@ -31805,21 +31327,21 @@ This can increase performance, and also silently ignore critical errors - ShaderText + Power - String + Float - String + Float - SourceName + ShaderText @@ -31833,14 +31355,14 @@ This can increase performance, and also silently ignore critical errors - Strength + SourceName - Int + String - Int + String @@ -31882,11 +31404,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianExampleShader - OBSGaussianExampleShader + Get-OBSFisheyeXyShader + OBSFisheyeXyShader Get - Get-OBSGaussianExampleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-InitialImage] <string>] [[-BeforeImage] <string>] [[-AfterImage] <string>] [[-TextColor] <string>] [[-MaxDistance] <float>] [[-Exp] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFisheyeXyShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-PowerX] <float>] [[-PowerY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -31894,37 +31416,23 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianExampleShader + Get-OBSFisheyeXyShader - ViewProj + CenterXPercent - System.Single[][] + Float - System.Single[][] + Float - Image - - - - - String - - String - - - - - - - ElapsedTime + CenterYPercent @@ -31937,120 +31445,8 @@ This can increase performance, and also silently ignore critical errors - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - InitialImage - - - - - String - - String - - - - - - - BeforeImage - - - - - String - - String - - - - - - - AfterImage - - - - - String - - String - - - - - - - TextColor - - - - - String - - String - - - - - - - MaxDistance + + PowerX @@ -32063,8 +31459,8 @@ This can increase performance, and also silently ignore critical errors - - Exp + + PowerY @@ -32077,7 +31473,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -32091,7 +31487,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -32105,7 +31501,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -32179,35 +31575,7 @@ This can increase performance, and also silently ignore critical errors - AfterImage - - - - - String - - String - - - - - - - BeforeImage - - - - - String - - String - - - - - - - ElapsedTime + CenterXPercent @@ -32221,7 +31589,7 @@ This can increase performance, and also silently ignore critical errors - Exp + CenterYPercent @@ -32263,35 +31631,35 @@ This can increase performance, and also silently ignore critical errors - Image + NoResponse - String + Switch - String + Switch - InitialImage + PassThru - String + Switch - String + Switch - MaxDistance + PowerX @@ -32305,28 +31673,14 @@ This can increase performance, and also silently ignore critical errors - NoResponse - - - - - Switch - - Switch - - - - - - - PassThru + PowerY - Switch + Float - Switch + Float @@ -32360,20 +31714,6 @@ This can increase performance, and also silently ignore critical errors - - TextColor - - - - - String - - String - - - - - UseShaderTime @@ -32388,76 +31728,6 @@ This can increase performance, and also silently ignore critical errors - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ViewProj - - - - - System.Single[][] - - System.Single[][] - - - - - @@ -32480,11 +31750,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianSimpleShader - OBSGaussianSimpleShader + Get-OBSFlipShader + OBSFlipShader Get - Get-OBSGaussianSimpleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Samples] <int>] [[-LOD] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFlipShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -32492,233 +31762,65 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGaussianSimpleShader - - ViewProj + Get-OBSFlipShader + + Horizontal - System.Single[][] + Switch - System.Single[][] + Switch - - Image + + Vertical - String + Switch - String + Switch - - ElapsedTime + + SourceName - Float + String - Float + String - - UvOffset + + FilterName - System.Single[] + String - System.Single[] + String - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - RandF - - - - - Float - - Float - - - - - - - RandInstanceF - - - - - Float - - Float - - - - - - - RandActivationF - - - - - Float - - Float - - - - - - - Loops - - - - - Int - - Int - - - - - - - LocalTime - - - - - Float - - Float - - - - - - - Samples - - - - - Int - - Int - - - - - - - LOD - - - - - Int - - Int - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText + + ShaderText @@ -32790,20 +31892,6 @@ This can increase performance, and also silently ignore critical errors - - ElapsedTime - - - - - Float - - Float - - - - - FilterName @@ -32833,56 +31921,14 @@ This can increase performance, and also silently ignore critical errors - Image - - - - - String - - String - - - - - - - LocalTime - - - - - Float - - Float - - - - - - - LOD - - - - - Int - - Int - - - - - - - Loops + Horizontal - Int + Switch - Int + Switch @@ -32916,62 +31962,6 @@ This can increase performance, and also silently ignore critical errors - - RandActivationF - - - - - Float - - Float - - - - - - - RandF - - - - - Float - - Float - - - - - - - RandInstanceF - - - - - Float - - Float - - - - - - - Samples - - - - - Int - - Int - - - - - ShaderText @@ -33015,70 +32005,14 @@ This can increase performance, and also silently ignore critical errors - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ViewProj + Vertical - System.Single[][] + Switch - System.Single[][] + Switch @@ -33106,11 +32040,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGbCameraShader - OBSGbCameraShader + Get-OBSFrostedGlassShader + OBSFrostedGlassShader Get - Get-OBSGbCameraShader [[-PixelSize] <float>] [[-DitherFactor] <float>] [[-Brightness] <float>] [[-Contrast] <float>] [[-Gamma] <float>] [[-Color1] <string>] [[-Color2] <string>] [[-Color3] <string>] [[-Color4] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-AlternativeBayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSFrostedGlassShader [[-AlphaPercent] <float>] [[-Amount] <float>] [[-Scale] <float>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animate] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -33118,9 +32052,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGbCameraShader + Get-OBSFrostedGlassShader - PixelSize + AlphaPercent @@ -33134,7 +32068,7 @@ This can increase performance, and also silently ignore critical errors - DitherFactor + Amount @@ -33147,36 +32081,8 @@ This can increase performance, and also silently ignore critical errors - - AlternativeBayer - - - - - Switch - - Switch - - - - - - Brightness - - - - - Float - - Float - - - - - - - Contrast + Scale @@ -33189,50 +32095,50 @@ This can increase performance, and also silently ignore critical errors - - Gamma + + Animate - Float + Switch - Float + Switch - - Color1 + + HorizontalBorder - String + Switch - String + Switch - - Color2 + + BorderOffset - String + Float - String + Float - - Color3 + + BorderColor @@ -33245,8 +32151,8 @@ This can increase performance, and also silently ignore critical errors - - Color4 + + Notes @@ -33259,7 +32165,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -33273,7 +32179,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -33287,7 +32193,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -33361,21 +32267,7 @@ This can increase performance, and also silently ignore critical errors - AlternativeBayer - - - - - Switch - - Switch - - - - - - - Brightness + AlphaPercent @@ -33389,49 +32281,35 @@ This can increase performance, and also silently ignore critical errors - Color1 - - - - - String - - String - - - - - - - Color2 + Amount - String + Float - String + Float - Color3 + Animate - String + Switch - String + Switch - Color4 + BorderColor @@ -33445,7 +32323,7 @@ This can increase performance, and also silently ignore critical errors - Contrast + BorderOffset @@ -33459,35 +32337,35 @@ This can increase performance, and also silently ignore critical errors - DitherFactor + FilterName - Float + String - Float + String - FilterName + Force - String + Switch - String + Switch - Force + HorizontalBorder @@ -33501,28 +32379,28 @@ This can increase performance, and also silently ignore critical errors - Gamma + NoResponse - Float + Switch - Float + Switch - NoResponse + Notes - Switch + String - Switch + String @@ -33543,7 +32421,7 @@ This can increase performance, and also silently ignore critical errors - PixelSize + Scale @@ -33620,11 +32498,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlassShader - OBSGlassShader + Get-OBSGammaCorrectionShader + OBSGammaCorrectionShader Get - Get-OBSGlassShader [[-AlphaPercent] <float>] [[-OffsetAmount] <float>] [[-XSize] <int>] [[-YSize] <int>] [[-ReflectionOffset] <int>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-GlassColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGammaCorrectionShader [[-Red] <float>] [[-Green] <float>] [[-Blue] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -33632,9 +32510,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlassShader + Get-OBSGammaCorrectionShader - AlphaPercent + Red @@ -33648,7 +32526,7 @@ This can increase performance, and also silently ignore critical errors - OffsetAmount + Green @@ -33662,63 +32540,7 @@ This can increase performance, and also silently ignore critical errors - XSize - - - - - Int - - Int - - - - - - - YSize - - - - - Int - - Int - - - - - - - ReflectionOffset - - - - - Int - - Int - - - - - - - HorizontalBorder - - - - - Switch - - Switch - - - - - - - BorderOffset + Blue @@ -33731,35 +32553,7 @@ This can increase performance, and also silently ignore critical errors - - BorderColor - - - - - String - - String - - - - - - - GlassColor - - - - - String - - String - - - - - - + Notes @@ -33773,7 +32567,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -33787,7 +32581,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -33801,7 +32595,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -33875,35 +32669,7 @@ This can increase performance, and also silently ignore critical errors - AlphaPercent - - - - - Float - - Float - - - - - - - BorderColor - - - - - String - - String - - - - - - - BorderOffset + Blue @@ -33945,28 +32711,14 @@ This can increase performance, and also silently ignore critical errors - GlassColor - - - - - String - - String - - - - - - - HorizontalBorder + Green - Switch + Float - Switch + Float @@ -34000,20 +32752,6 @@ This can increase performance, and also silently ignore critical errors - - OffsetAmount - - - - - Float - - Float - - - - - PassThru @@ -34029,14 +32767,14 @@ This can increase performance, and also silently ignore critical errors - ReflectionOffset + Red - Int + Float - Int + Float @@ -34084,34 +32822,6 @@ This can increase performance, and also silently ignore critical errors - - XSize - - - - - Int - - Int - - - - - - - YSize - - - - - Int - - Int - - - - - @@ -34134,11 +32844,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlitchAnalogShader - OBSGlitchAnalogShader + Get-OBSGaussianBlurAdvancedShader + OBSGaussianBlurAdvancedShader Get - Get-OBSGlitchAnalogShader [[-ScanLineJitterDisplacement] <float>] [[-ScanLineJitterThresholdPercent] <int>] [[-VerticalJumpAmount] <float>] [[-VerticalSpeed] <float>] [[-HorizontalShake] <float>] [[-ColorDriftAmount] <float>] [[-ColorDriftSpeed] <float>] [[-PulseSpeedPercent] <int>] [[-AlphaPercent] <int>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-RotateColors] [-ApplyToAlphaLayer] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGaussianBlurAdvancedShader [[-Directions] <float>] [[-Quality] <float>] [[-Size] <float>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -34146,9 +32856,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlitchAnalogShader + Get-OBSGaussianBlurAdvancedShader - ScanLineJitterDisplacement + Directions @@ -34162,21 +32872,21 @@ This can increase performance, and also silently ignore critical errors - ScanLineJitterThresholdPercent + Quality - Int + Float - Int + Float - VerticalJumpAmount + Size @@ -34190,7 +32900,7 @@ This can increase performance, and also silently ignore critical errors - VerticalSpeed + MaskLeft @@ -34204,7 +32914,7 @@ This can increase performance, and also silently ignore critical errors - HorizontalShake + MaskRight @@ -34218,7 +32928,7 @@ This can increase performance, and also silently ignore critical errors - ColorDriftAmount + MaskTop @@ -34232,7 +32942,7 @@ This can increase performance, and also silently ignore critical errors - ColorDriftSpeed + MaskBottom @@ -34245,119 +32955,7 @@ This can increase performance, and also silently ignore critical errors - - PulseSpeedPercent - - - - - Int - - Int - - - - - - - AlphaPercent - - - - - Int - - Int - - - - - - - RotateColors - - - - - Switch - - Switch - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - ReplaceImageColor - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorToReplace - - - - - String - - String - - - - - - - Notes - - - - - String - - String - - - - - - + SourceName @@ -34371,7 +32969,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -34385,7 +32983,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -34459,63 +33057,7 @@ This can increase performance, and also silently ignore critical errors - AlphaPercent - - - - - Int - - Int - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorDriftAmount - - - - - Float - - Float - - - - - - - ColorDriftSpeed + Directions @@ -34528,20 +33070,6 @@ This can increase performance, and also silently ignore critical errors - - ColorToReplace - - - - - String - - String - - - - - FilterName @@ -34571,7 +33099,7 @@ This can increase performance, and also silently ignore critical errors - HorizontalShake + MaskBottom @@ -34585,63 +33113,49 @@ This can increase performance, and also silently ignore critical errors - NoResponse - - - - - Switch - - Switch - - - - - - - Notes + MaskLeft - String + Float - String + Float - PassThru + MaskRight - Switch + Float - Switch + Float - PulseSpeedPercent + MaskTop - Int + Float - Int + Float - ReplaceImageColor + NoResponse @@ -34655,7 +33169,7 @@ This can increase performance, and also silently ignore critical errors - RotateColors + PassThru @@ -34669,7 +33183,7 @@ This can increase performance, and also silently ignore critical errors - ScanLineJitterDisplacement + Quality @@ -34683,28 +33197,28 @@ This can increase performance, and also silently ignore critical errors - ScanLineJitterThresholdPercent + ShaderText - Int + String - Int + String - ShaderText + Size - String + Float - String + Float @@ -34738,34 +33252,6 @@ This can increase performance, and also silently ignore critical errors - - VerticalJumpAmount - - - - - Float - - Float - - - - - - - VerticalSpeed - - - - - Float - - Float - - - - - @@ -34788,11 +33274,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlitchShader - OBSGlitchShader + Get-OBSGaussianBlurShader + OBSGaussianBlurShader Get - Get-OBSGlitchShader [[-AMT] <float>] [[-SPEED] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGaussianBlurShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ImageSize] <float[]>] [[-ImageTexel] <float[]>] [[-URadius] <int>] [[-UDiameter] <int>] [[-UTexelDelta] <float[]>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-Kernel] <string>] [[-KernelTexel] <float[]>] [[-PixelSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -34800,37 +33286,163 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlitchShader + Get-OBSGaussianBlurShader - AMT + ViewProj - Float + System.Single[][] - Float + System.Single[][] - SPEED + Image - Float + String - Float + String - - SourceName + + ImageSize + + + + + System.Single[] + + System.Single[] + + + + + + + ImageTexel + + + + + System.Single[] + + System.Single[] + + + + + + + URadius + + + + + Int + + Int + + + + + + + UDiameter + + + + + Int + + Int + + + + + + + UTexelDelta + + + + + System.Single[] + + System.Single[] + + + + + + + ElapsedTime + + + + + Float + + Float + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + Kernel @@ -34843,7 +33455,49 @@ This can increase performance, and also silently ignore critical errors - + + KernelTexel + + + + + System.Single[] + + System.Single[] + + + + + + + PixelSize + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + FilterName @@ -34857,7 +33511,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -34931,7 +33585,7 @@ This can increase performance, and also silently ignore critical errors - AMT + ElapsedTime @@ -34972,6 +33626,76 @@ This can increase performance, and also silently ignore critical errors + + Image + + + + + String + + String + + + + + + + ImageSize + + + + + System.Single[] + + System.Single[] + + + + + + + ImageTexel + + + + + System.Single[] + + System.Single[] + + + + + + + Kernel + + + + + String + + String + + + + + + + KernelTexel + + + + + System.Single[] + + System.Single[] + + + + + NoResponse @@ -35000,6 +33724,20 @@ This can increase performance, and also silently ignore critical errors + + PixelSize + + + + + Float + + Float + + + + + ShaderText @@ -35029,14 +33767,28 @@ This can increase performance, and also silently ignore critical errors - SPEED + UDiameter - Float + Int - Float + Int + + + + + + + URadius + + + + + Int + + Int @@ -35056,6 +33808,76 @@ This can increase performance, and also silently ignore critical errors + + UTexelDelta + + + + + System.Single[] + + System.Single[] + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + @@ -35078,11 +33900,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlowShader - OBSGlowShader + Get-OBSGaussianBlurSimpleShader + OBSGaussianBlurSimpleShader Get - Get-OBSGlowShader [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeed] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGaussianBlurSimpleShader [[-Strength] <int>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -35090,9 +33912,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGlowShader + Get-OBSGaussianBlurSimpleShader - GlowPercent + Strength @@ -35106,90 +33928,62 @@ This can increase performance, and also silently ignore critical errors - Blur + MaskLeft - Int + Float - Int + Float - MinBrightness + MaskRight - Int + Float - Int + Float - MaxBrightness + MaskTop - Int + Float - Int + Float - PulseSpeed - - - - - Int - - Int - - - - - - - Ease - - - - - Switch - - Switch - - - - - - - Notes + MaskBottom - String + Float - String + Float - + SourceName @@ -35203,7 +33997,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -35217,7 +34011,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -35290,34 +34084,6 @@ This can increase performance, and also silently ignore critical errors - - Blur - - - - - Int - - Int - - - - - - - Ease - - - - - Switch - - Switch - - - - - FilterName @@ -35347,70 +34113,70 @@ This can increase performance, and also silently ignore critical errors - GlowPercent + MaskBottom - Int + Float - Int + Float - MaxBrightness + MaskLeft - Int + Float - Int + Float - MinBrightness + MaskRight - Int + Float - Int + Float - NoResponse + MaskTop - Switch + Float - Switch + Float - Notes + NoResponse - String + Switch - String + Switch @@ -35431,21 +34197,21 @@ This can increase performance, and also silently ignore critical errors - PulseSpeed + ShaderText - Int + String - Int + String - ShaderText + SourceName @@ -35459,14 +34225,14 @@ This can increase performance, and also silently ignore critical errors - SourceName + Strength - String + Int - String + Int @@ -35508,11 +34274,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGradientShader - OBSGradientShader + Get-OBSGaussianExampleShader + OBSGaussianExampleShader Get - Get-OBSGradientShader [[-StartColor] <string>] [[-StartStep] <float>] [[-MiddleColor] <string>] [[-MiddleStep] <float>] [[-EndColor] <string>] [[-EndStep] <float>] [[-AlphaPercent] <int>] [[-PulseSpeed] <int>] [[-ColorToReplace] <string>] [[-GradientCenterWidthPercentage] <int>] [[-GradientCenterHeightPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-RotateColors] [-ApplyToAlphaLayer] [-ApplyToSpecificColor] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGaussianExampleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-InitialImage] <string>] [[-BeforeImage] <string>] [[-AfterImage] <string>] [[-TextColor] <string>] [[-MaxDistance] <float>] [[-Exp] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -35520,177 +34286,121 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGradientShader + Get-OBSGaussianExampleShader - StartColor + ViewProj - String + System.Single[][] - String + System.Single[][] - StartStep + Image - Float + String - Float + String - MiddleColor + ElapsedTime - String + Float - String + Float - MiddleStep + UvOffset - Float + System.Single[] - Float + System.Single[] - EndColor + UvScale - String + System.Single[] - String + System.Single[] - EndStep + UvSize - Float + System.Single[] - Float + System.Single[] - AlphaPercent + UvPixelInterval - Int + System.Single[] - Int + System.Single[] - PulseSpeed - - - - - Int - - Int - - - - - - - Ease - - - - - Switch - - Switch - - - - - - - RotateColors - - - - - Switch - - Switch - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor + InitialImage - Switch + String - Switch + String - ColorToReplace + BeforeImage @@ -35703,77 +34413,63 @@ This can increase performance, and also silently ignore critical errors - - Horizontal - - - - - Switch - - Switch - - - - - - - Vertical + + AfterImage - Switch + String - Switch + String - - GradientCenterWidthPercentage + + TextColor - Int + String - Int + String - - GradientCenterHeightPercentage + + MaxDistance - Int + Float - Int + Float - - Notes + + Exp - String + Float - String + Float - + SourceName @@ -35787,7 +34483,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -35801,7 +34497,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -35875,49 +34571,7 @@ This can increase performance, and also silently ignore critical errors - AlphaPercent - - - - - Int - - Int - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorToReplace + AfterImage @@ -35931,35 +34585,35 @@ This can increase performance, and also silently ignore critical errors - Ease + BeforeImage - Switch + String - Switch + String - EndColor + ElapsedTime - String + Float - String + Float - EndStep + Exp @@ -36001,49 +34655,21 @@ This can increase performance, and also silently ignore critical errors - GradientCenterHeightPercentage - - - - - Int - - Int - - - - - - - GradientCenterWidthPercentage - - - - - Int - - Int - - - - - - - Horizontal + Image - Switch + String - Switch + String - MiddleColor + InitialImage @@ -36057,7 +34683,7 @@ This can increase performance, and also silently ignore critical errors - MiddleStep + MaxDistance @@ -36085,140 +34711,140 @@ This can increase performance, and also silently ignore critical errors - Notes + PassThru - String + Switch - String + Switch - PassThru + ShaderText - Switch + String - Switch + String - PulseSpeed + SourceName - Int + String - Int + String - RotateColors + TextColor - Switch + String - Switch + String - ShaderText + UseShaderTime - String + Switch - String + Switch - SourceName + UvOffset - String + System.Single[] - String + System.Single[] - StartColor + UvPixelInterval - String + System.Single[] - String + System.Single[] - StartStep + UvScale - Float + System.Single[] - Float + System.Single[] - UseShaderTime + UvSize - Switch + System.Single[] - Switch + System.Single[] - Vertical + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] @@ -36246,253 +34872,119 @@ This can increase performance, and also silently ignore critical errors - Get-OBSGroup - OBSGroup + Get-OBSGaussianSimpleShader + OBSGaussianSimpleShader Get - Get-OBSGroup : GetGroupList + Get-OBSGaussianSimpleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Samples] <int>] [[-LOD] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets an array of all groups in OBS. - Groups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can. - Get-OBSGroup calls the OBS WebSocket with a request of type GetGroupList. - Get-OBSGroup - - PassThru + Get-OBSGaussianSimpleShader + + ViewProj - If set, will return the information that would otherwise be sent to OBS. + + - Switch + System.Single[][] - Switch + System.Single[][] - - NoResponse + + Image - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + String - Switch + String - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSGroup - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getgrouplist - - - - - - Get-OBSGroupSceneItem - OBSGroupSceneItem - Get - - Get-OBSGroupSceneItem : GetGroupSceneItemList - - 0.2.0.1 - - - Basically GetSceneItemList, but for groups. - Using groups at all in OBS is discouraged, as they are very broken under the hood. Please use nested scenes instead. - Groups only - Get-OBSGroupSceneItem calls the OBS WebSocket with a request of type GetGroupSceneItemList. - - - - Get-OBSGroupSceneItem - - SceneName + + ElapsedTime - Name of the group to get the items of + + - String + Float - String + Float - - SceneUuid + + UvOffset - UUID of the group to get the items of + + - String + System.Single[] - String + System.Single[] - - PassThru + + UvScale - If set, will return the information that would otherwise be sent to OBS. + + - Switch + System.Single[] - Switch + System.Single[] - - NoResponse + + UvPixelInterval - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + System.Single[] - Switch + System.Single[] - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - SceneName - - Name of the group to get the items of - - String - - String - - - - - - - SceneUuid - - UUID of the group to get the items of - - String - - String - - - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getgroupsceneitemlist - - - - - - Get-OBSHalftoneShader - OBSHalftoneShader - Get - - Get-OBSHalftoneShader [[-Threshold] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSHalftoneShader - - Threshold + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + RandF @@ -36505,7 +34997,91 @@ This can increase performance, and also silently ignore critical errors - + + RandInstanceF + + + + + Float + + Float + + + + + + + RandActivationF + + + + + Float + + Float + + + + + + + Loops + + + + + Int + + Int + + + + + + + LocalTime + + + + + Float + + Float + + + + + + + Samples + + + + + Int + + Int + + + + + + + LOD + + + + + Int + + Int + + + + + + SourceName @@ -36519,7 +35095,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -36533,7 +35109,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -36606,6 +35182,20 @@ This can increase performance, and also silently ignore critical errors + + ElapsedTime + + + + + Float + + Float + + + + + FilterName @@ -36635,77 +35225,77 @@ This can increase performance, and also silently ignore critical errors - NoResponse + Image - Switch + String - Switch + String - PassThru + LocalTime - Switch + Float - Switch + Float - ShaderText + LOD - String + Int - String + Int - SourceName + Loops - String + Int - String + Int - Threshold + NoResponse - Float + Switch - Float + Switch - UseShaderTime + PassThru @@ -36718,193 +35308,95 @@ This can increase performance, and also silently ignore critical errors - - - + + RandActivationF + + + + + Float - System.String + Float - - - - - + + + + + RandF + + + + + Float - System.Object + Float - - - - - - - Get-OBSHardBlinkShader - OBSHardBlinkShader - Get - - -Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - - 0.2.0.1 - - - - - - Get-OBSHardBlinkShader - - Timeon - - - Float - - Float - - - - - - - Timeoff - - - Float - - Float - - - - - - - SourceName - - - String - - String - - - - - - - FilterName - - - String - - String - - - - - - - ShaderText - - - String - - String - - - - - - - Force - - - Switch - - Switch - - - - - - - PassThru - - - Switch - - Switch - - - - - - - NoResponse - - - Switch - - Switch - - - - - - - UseShaderTime - - - Switch - - Switch - - - - - - - - + + + - FilterName + RandInstanceF + + - String + Float - String + Float - Force + Samples + + - Switch + Int - Switch + Int - NoResponse + ShaderText + + + + + String + + String + + + + + + + SourceName + + - Switch + String - Switch + String - PassThru + UseShaderTime + + Switch @@ -36915,60 +35407,70 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - ShaderText + UvOffset + + - String + System.Single[] - String + System.Single[] - SourceName + UvPixelInterval + + - String + System.Single[] - String + System.Single[] - Timeoff + UvScale + + - Float + System.Single[] - Float + System.Single[] - Timeon + UvSize + + - Float + System.Single[] - Float + System.Single[] - UseShaderTime + ViewProj + + - Switch + System.Single[][] - Switch + System.Single[][] @@ -36996,11 +35498,11 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Get-OBSHeatWaveSimpleShader - OBSHeatWaveSimpleShader + Get-OBSGbCameraShader + OBSGbCameraShader Get - Get-OBSHeatWaveSimpleShader [[-Rate] <float>] [[-Strength] <float>] [[-Distortion] <float>] [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGbCameraShader [[-PixelSize] <float>] [[-DitherFactor] <float>] [[-Brightness] <float>] [[-Contrast] <float>] [[-Gamma] <float>] [[-Color1] <string>] [[-Color2] <string>] [[-Color3] <string>] [[-Color4] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-AlternativeBayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -37008,9 +35510,9 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Get-OBSHeatWaveSimpleShader + Get-OBSGbCameraShader - Rate + PixelSize @@ -37024,7 +35526,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Strength + DitherFactor @@ -37037,8 +35539,22 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S + + AlternativeBayer + + + + + Switch + + Switch + + + + + - Distortion + Brightness @@ -37052,7 +35568,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Opacity + Contrast @@ -37065,8 +35581,22 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - SourceName + + Gamma + + + + + Float + + Float + + + + + + + Color1 @@ -37079,8 +35609,8 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - FilterName + + Color2 @@ -37093,8 +35623,8 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - ShaderText + + Color3 @@ -37107,50 +35637,64 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - Force + + Color4 - Switch + String - Switch + String - - PassThru + + SourceName - Switch + String - Switch + String - - NoResponse + + FilterName - Switch + String - Switch + String + + + + + + + ShaderText + + + + + String + + String - UseShaderTime + Force @@ -37163,11 +35707,67 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + - Distortion + AlternativeBayer + + + + + Switch + + Switch + + + + + + + Brightness @@ -37181,7 +35781,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - FilterName + Color1 @@ -37195,35 +35795,49 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Force + Color2 - Switch + String - Switch + String - NoResponse + Color3 - Switch + String - Switch + String - Opacity + Color4 + + + + + String + + String + + + + + + + Contrast @@ -37237,7 +35851,35 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - PassThru + DitherFactor + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force @@ -37251,7 +35893,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Rate + Gamma @@ -37265,35 +35907,35 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - ShaderText + NoResponse - String + Switch - String + Switch - SourceName + PassThru - String + Switch - String + Switch - Strength + PixelSize @@ -37306,6 +35948,34 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + UseShaderTime @@ -37342,11 +36012,11 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Get-OBSHexagonShader - OBSHexagonShader + Get-OBSGlassShader + OBSGlassShader Get - Get-OBSHexagonShader [[-HexColor] <string>] [[-AlphaPercent] <int>] [[-Quantity] <float>] [[-BorderWidth] <int>] [[-SpeedPercent] <int>] [[-DistortX] <float>] [[-DistortY] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Blend] [-Equilateral] [-ZoomAnimate] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSGlassShader [[-AlphaPercent] <float>] [[-OffsetAmount] <float>] [[-XSize] <int>] [[-YSize] <int>] [[-ReflectionOffset] <int>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-GlassColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -37354,37 +36024,23 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Get-OBSHexagonShader + Get-OBSGlassShader - HexColor - - - - - String - - String - - - - - - AlphaPercent - Int + Float - Int + Float - - Quantity + + OffsetAmount @@ -37397,8 +36053,8 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - BorderWidth + + XSize @@ -37411,50 +36067,22 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - - Blend - - - - - Switch - - Switch - - - - - - - Equilateral - - - - - Switch - - Switch - - - - - - - ZoomAnimate + + YSize - Switch + Int - Switch + Int - SpeedPercent + ReflectionOffset @@ -37468,7 +36096,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Glitch + HorizontalBorder @@ -37482,7 +36110,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - DistortX + BorderOffset @@ -37496,48 +36124,34 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - DistortY + BorderColor - Float + String - Float + String - OffsetX + GlassColor - Float + String - Float + String - OffsetY - - - - - Float - - Float - - - - - - Notes @@ -37551,7 +36165,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - + SourceName @@ -37565,7 +36179,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - + FilterName @@ -37579,7 +36193,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - + ShaderText @@ -37658,48 +36272,6 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Int - - Int - - - - - - - Blend - - - - - Switch - - Switch - - - - - - - BorderWidth - - - - - Int - - Int - - - - - - - DistortX - - - - Float Float @@ -37709,28 +36281,28 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - DistortY + BorderColor - Float + String - Float + String - Equilateral + BorderOffset - Switch + Float - Switch + Float @@ -37765,28 +36337,28 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Glitch + GlassColor - Switch + String - Switch + String - HexColor + HorizontalBorder - String + Switch - String + Switch @@ -37821,21 +36393,7 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - OffsetX - - - - - Float - - Float - - - - - - - OffsetY + OffsetAmount @@ -37863,14 +36421,14 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Quantity + ReflectionOffset - Float + Int - Float + Int @@ -37905,42 +36463,42 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - SpeedPercent + UseShaderTime - Int + Switch - Int + Switch - UseShaderTime + XSize - Switch + Int - Switch + Int - ZoomAnimate + YSize - Switch + Int - Switch + Int @@ -37968,116 +36526,49 @@ Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-S - Get-OBSHotkey - OBSHotkey + Get-OBSGlitchAnalogShader + OBSGlitchAnalogShader Get - Get-OBSHotkey : GetHotkeyList + Get-OBSGlitchAnalogShader [[-ScanLineJitterDisplacement] <float>] [[-ScanLineJitterThresholdPercent] <int>] [[-VerticalJumpAmount] <float>] [[-VerticalSpeed] <float>] [[-HorizontalShake] <float>] [[-ColorDriftAmount] <float>] [[-ColorDriftSpeed] <float>] [[-PulseSpeedPercent] <int>] [[-AlphaPercent] <int>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-RotateColors] [-ApplyToAlphaLayer] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets an array of all hotkey names in OBS. - Note: Hotkey functionality in obs-websocket comes as-is, and we do not guarantee support if things are broken. In 9/10 usages of hotkey requests, there exists a better, more reliable method via other requests. - Get-OBSHotkey calls the OBS WebSocket with a request of type GetHotkeyList. - Get-OBSHotkey - - PassThru + Get-OBSGlitchAnalogShader + + ScanLineJitterDisplacement - If set, will return the information that would otherwise be sent to OBS. + + - Switch + Float - Switch + Float - - NoResponse + + ScanLineJitterThresholdPercent - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + Int - Switch + Int - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSHotkey - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#gethotkeylist - - - - - - Get-OBSHslHsvSaturationShader - OBSHslHsvSaturationShader - Get - - Get-OBSHslHsvSaturationShader [[-HslSaturationFactor] <float>] [[-HslGamma] <float>] [[-HsvSaturationFactor] <float>] [[-HsvGamma] <float>] [[-AdjustmentOrder] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSHslHsvSaturationShader - - HslSaturationFactor + + VerticalJumpAmount @@ -38090,8 +36581,8 @@ This can increase performance, and also silently ignore critical errors - - HslGamma + + VerticalSpeed @@ -38104,8 +36595,8 @@ This can increase performance, and also silently ignore critical errors - - HsvSaturationFactor + + HorizontalShake @@ -38118,8 +36609,8 @@ This can increase performance, and also silently ignore critical errors - - HsvGamma + + ColorDriftAmount @@ -38132,8 +36623,22 @@ This can increase performance, and also silently ignore critical errors - - AdjustmentOrder + + ColorDriftSpeed + + + + + Float + + Float + + + + + + + PulseSpeedPercent @@ -38146,7 +36651,105 @@ This can increase performance, and also silently ignore critical errors - + + AlphaPercent + + + + + Int + + Int + + + + + + + RotateColors + + + + + Switch + + Switch + + + + + + + ApplyToAlphaLayer + + + + + Switch + + Switch + + + + + + + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + + + ColorToReplace + + + + + String + + String + + + + + + + Notes + + + + + String + + String + + + + + + SourceName @@ -38160,7 +36763,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -38174,7 +36777,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -38248,7 +36851,7 @@ This can increase performance, and also silently ignore critical errors - AdjustmentOrder + AlphaPercent @@ -38262,21 +36865,21 @@ This can increase performance, and also silently ignore critical errors - FilterName + ApplyToAlphaLayer - String + Switch - String + Switch - Force + ApplyToSpecificColor @@ -38290,7 +36893,7 @@ This can increase performance, and also silently ignore critical errors - HslGamma + ColorDriftAmount @@ -38304,7 +36907,7 @@ This can increase performance, and also silently ignore critical errors - HslSaturationFactor + ColorDriftSpeed @@ -38318,21 +36921,49 @@ This can increase performance, and also silently ignore critical errors - HsvGamma + ColorToReplace - Float + String - Float + String - HsvSaturationFactor + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + HorizontalShake @@ -38359,6 +36990,20 @@ This can increase performance, and also silently ignore critical errors + + Notes + + + + + String + + String + + + + + PassThru @@ -38373,6 +37018,76 @@ This can increase performance, and also silently ignore critical errors + + PulseSpeedPercent + + + + + Int + + Int + + + + + + + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + RotateColors + + + + + Switch + + Switch + + + + + + + ScanLineJitterDisplacement + + + + + Float + + Float + + + + + + + ScanLineJitterThresholdPercent + + + + + Int + + Int + + + + + ShaderText @@ -38415,6 +37130,34 @@ This can increase performance, and also silently ignore critical errors + + VerticalJumpAmount + + + + + Float + + Float + + + + + + + VerticalSpeed + + + + + Float + + Float + + + + + @@ -38437,11 +37180,13 @@ This can increase performance, and also silently ignore critical errors - Get-OBSHueRotatonShader - OBSHueRotatonShader + Get-OBSGlitchPeriodicShader + OBSGlitchPeriodicShader Get - Get-OBSHueRotatonShader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HueOverride] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-AMPL] <float>] [[-SCRA] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -38449,12 +37194,10 @@ This can increase performance, and also silently ignore critical errors - Get-OBSHueRotatonShader + Get-OBSGlitchPeriodicShader - Speed + PERI - - Float @@ -38464,25 +37207,45 @@ This can increase performance, and also silently ignore critical errors - - HueOverride + + DURA - - - Switch + Float - Switch + Float - + + AMPL + + + Float + + Float + + + + + + + SCRA + + + Float + + Float + + + + + + SourceName - - String @@ -38492,11 +37255,9 @@ This can increase performance, and also silently ignore critical errors - + FilterName - - String @@ -38506,11 +37267,9 @@ This can increase performance, and also silently ignore critical errors - + ShaderText - - String @@ -38523,8 +37282,6 @@ This can increase performance, and also silently ignore critical errors Force - - Switch @@ -38537,8 +37294,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -38551,8 +37306,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -38565,8 +37318,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -38580,38 +37331,44 @@ This can increase performance, and also silently ignore critical errors - FilterName + AMPL - - - String + Float - String + Float - Force + DURA - - - Switch + Float - Switch + Float - HueOverride + FilterName + + + String + + String + + + + + + + Force - - Switch @@ -38624,8 +37381,6 @@ This can increase performance, and also silently ignore critical errors NoResponse - - Switch @@ -38638,8 +37393,6 @@ This can increase performance, and also silently ignore critical errors PassThru - - Switch @@ -38650,24 +37403,32 @@ This can increase performance, and also silently ignore critical errors - ShaderText + PERI - - - String + Float - String + Float - SourceName + SCRA + + + Float + + Float + + + + + + + ShaderText - - String @@ -38678,14 +37439,12 @@ This can increase performance, and also silently ignore critical errors - Speed + SourceName - - - Float + String - Float + String @@ -38694,8 +37453,6 @@ This can increase performance, and also silently ignore critical errors UseShaderTime - - Switch @@ -38727,134 +37484,66 @@ This can increase performance, and also silently ignore critical errors - Get-OBSInput - OBSInput + Get-OBSGlitchShader + OBSGlitchShader Get - Get-OBSInput : GetInputList + Get-OBSGlitchShader [[-AMT] <float>] [[-SPEED] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets an array of all inputs in OBS. - Get-OBSInput calls the OBS WebSocket with a request of type GetInputList. - Get-OBSInput - - InputKind + Get-OBSGlitchShader + + AMT - Restrict the array to only inputs of the specified kind + + - String + Float - String + Float - - PassThru + + SPEED - If set, will return the information that would otherwise be sent to OBS. + + - Switch + Float - Switch + Float - - NoResponse + + SourceName - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + String - Switch + String - - - - - InputKind - - Restrict the array to only inputs of the specified kind - - String - - String - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputlist - - - - - - Get-OBSInputAudioBalance - OBSInputAudioBalance - Get - - Get-OBSInputAudioBalance : GetInputAudioBalance - - 0.2.0.1 - - - Gets the audio balance of an input. - Get-OBSInputAudioBalance calls the OBS WebSocket with a request of type GetInputAudioBalance. - - - - Get-OBSInputAudioBalance - - InputName + + FilterName - Name of the input to get the audio balance of + + String @@ -38864,10 +37553,11 @@ This can increase performance, and also silently ignore critical errors - - InputUuid + + ShaderText - UUID of the input to get the audio balance of + + String @@ -38877,10 +37567,11 @@ This can increase performance, and also silently ignore critical errors - - PassThru + + Force - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -38890,11 +37581,11 @@ This can increase performance, and also silently ignore critical errors - - NoResponse + + PassThru - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -38904,122 +37595,11 @@ This can increase performance, and also silently ignore critical errors - - - - - InputName - - Name of the input to get the audio balance of - - String - - String - - - - - - - InputUuid - - UUID of the input to get the audio balance of - - String - - String - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiobalance - - - - - - Get-OBSInputAudioMonitorType - OBSInputAudioMonitorType - Get - - Get-OBSInputAudioMonitorType : GetInputAudioMonitorType - - 0.2.0.1 - - - Gets the audio monitor type of an input. - The available audio monitor types are: - - `OBS_MONITORING_TYPE_NONE` - - `OBS_MONITORING_TYPE_MONITOR_ONLY` - - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT` - Get-OBSInputAudioMonitorType calls the OBS WebSocket with a request of type GetInputAudioMonitorType. - - - - Get-OBSInputAudioMonitorType - - InputName - - Name of the input to get the audio monitor type of - - String - - String - - - - - - - InputUuid - - UUID of the input to get the audio monitor type of - - String - - String - - - - - - - PassThru + + NoResponse - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39029,11 +37609,11 @@ This can increase performance, and also silently ignore critical errors - - NoResponse + + UseShaderTime - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39047,22 +37627,24 @@ This can increase performance, and also silently ignore critical errors - InputName + AMT - Name of the input to get the audio monitor type of + + - String + Float - String + Float - InputUuid + FilterName - UUID of the input to get the audio monitor type of + + String @@ -39072,11 +37654,25 @@ This can increase performance, and also silently ignore critical errors + + Force + + + + + Switch + + Switch + + + + + NoResponse - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39089,7 +37685,8 @@ This can increase performance, and also silently ignore critical errors PassThru - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39099,93 +37696,11 @@ This can increase performance, and also silently ignore critical errors - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiomonitortype - - - - - - Get-OBSInputAudioSyncOffset - OBSInputAudioSyncOffset - Get - - Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset - - 0.2.0.1 - - - Gets the audio sync offset of an input. - Note: The audio sync offset can be negative too! - Get-OBSInputAudioSyncOffset calls the OBS WebSocket with a request of type GetInputAudioSyncOffset. - - - - Get-OBSInputAudioSyncOffset - - InputName - - Name of the input to get the audio sync offset of - - String - - String - - - - - - - InputUuid - - UUID of the input to get the audio sync offset of - - String - - String - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - InputName + ShaderText - Name of the input to get the audio sync offset of + + String @@ -39196,9 +37711,10 @@ This can increase performance, and also silently ignore critical errors - InputUuid + SourceName - UUID of the input to get the audio sync offset of + + String @@ -39209,23 +37725,24 @@ This can increase performance, and also silently ignore critical errors - NoResponse + SPEED - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + Float - Switch + Float - PassThru + UseShaderTime - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39236,75 +37753,5598 @@ This can increase performance, and also silently ignore critical errors - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiosyncoffset - - + + + + System.String + + + + + + + + + System.Object + + + + + - Get-OBSInputAudioTracks - OBSInputAudioTracks + Get-OBSGlowShader + OBSGlowShader Get - Get-OBSInputAudioTracks : GetInputAudioTracks + Get-OBSGlowShader [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeed] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets the enable state of all audio tracks of an input. - Get-OBSInputAudioTracks calls the OBS WebSocket with a request of type GetInputAudioTracks. - Get-OBSInputAudioTracks - - InputName + Get-OBSGlowShader + + GlowPercent - Name of the input + + - String + Int - String + Int - - InputUuid + + Blur - UUID of the input + + - String + Int - String + Int - - PassThru + + MinBrightness - If set, will return the information that would otherwise be sent to OBS. + + - Switch + Int - Switch + Int - + + MaxBrightness + + + + + Int + + Int + + + + + + + PulseSpeed + + + + + Int + + Int + + + + + + + Ease + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + Blur + + + + + Int + + Int + + + + + + + Ease + + + + + Switch + + Switch + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + GlowPercent + + + + + Int + + Int + + + + + + + MaxBrightness + + + + + Int + + Int + + + + + + + MinBrightness + + + + + Int + + Int + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + PulseSpeed + + + + + Int + + Int + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSGradientShader + OBSGradientShader + Get + + Get-OBSGradientShader [[-StartColor] <string>] [[-StartStep] <float>] [[-MiddleColor] <string>] [[-MiddleStep] <float>] [[-EndColor] <string>] [[-EndStep] <float>] [[-AlphaPercent] <int>] [[-PulseSpeed] <int>] [[-ColorToReplace] <string>] [[-GradientCenterWidthPercentage] <int>] [[-GradientCenterHeightPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-RotateColors] [-ApplyToAlphaLayer] [-ApplyToSpecificColor] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSGradientShader + + StartColor + + + + + String + + String + + + + + + + StartStep + + + + + Float + + Float + + + + + + + MiddleColor + + + + + String + + String + + + + + + + MiddleStep + + + + + Float + + Float + + + + + + + EndColor + + + + + String + + String + + + + + + + EndStep + + + + + Float + + Float + + + + + + + AlphaPercent + + + + + Int + + Int + + + + + + + PulseSpeed + + + + + Int + + Int + + + + + + + Ease + + + + + Switch + + Switch + + + + + + + RotateColors + + + + + Switch + + Switch + + + + + + + ApplyToAlphaLayer + + + + + Switch + + Switch + + + + + + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + + + ColorToReplace + + + + + String + + String + + + + + + + Horizontal + + + + + Switch + + Switch + + + + + + + Vertical + + + + + Switch + + Switch + + + + + + + GradientCenterWidthPercentage + + + + + Int + + Int + + + + + + + GradientCenterHeightPercentage + + + + + Int + + Int + + + + + + + Notes + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + AlphaPercent + + + + + Int + + Int + + + + + + + ApplyToAlphaLayer + + + + + Switch + + Switch + + + + + + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + + + ColorToReplace + + + + + String + + String + + + + + + + Ease + + + + + Switch + + Switch + + + + + + + EndColor + + + + + String + + String + + + + + + + EndStep + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + GradientCenterHeightPercentage + + + + + Int + + Int + + + + + + + GradientCenterWidthPercentage + + + + + Int + + Int + + + + + + + Horizontal + + + + + Switch + + Switch + + + + + + + MiddleColor + + + + + String + + String + + + + + + + MiddleStep + + + + + Float + + Float + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + PulseSpeed + + + + + Int + + Int + + + + + + + RotateColors + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + StartColor + + + + + String + + String + + + + + + + StartStep + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + Vertical + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSGroup + OBSGroup + Get + + Get-OBSGroup : GetGroupList + + 0.2.0.1 + + + Gets an array of all groups in OBS. + Groups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can. + Get-OBSGroup calls the OBS WebSocket with a request of type GetGroupList. + + + + Get-OBSGroup + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSGroup + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getgrouplist + + + + + + Get-OBSGroupSceneItem + OBSGroupSceneItem + Get + + Get-OBSGroupSceneItem : GetGroupSceneItemList + + 0.2.0.1 + + + Basically GetSceneItemList, but for groups. + Using groups at all in OBS is discouraged, as they are very broken under the hood. Please use nested scenes instead. + Groups only + Get-OBSGroupSceneItem calls the OBS WebSocket with a request of type GetGroupSceneItemList. + + + + Get-OBSGroupSceneItem + + SceneName + + Name of the group to get the items of + + String + + String + + + + + + + SceneUuid + + UUID of the group to get the items of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + SceneName + + Name of the group to get the items of + + String + + String + + + + + + + SceneUuid + + UUID of the group to get the items of + + String + + String + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getgroupsceneitemlist + + + + + + Get-OBSHalftoneShader + OBSHalftoneShader + Get + + Get-OBSHalftoneShader [[-Threshold] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHalftoneShader + + Threshold + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Threshold + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSHardBlinkShader + OBSHardBlinkShader + Get + + Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHardBlinkShader + + Timeon + + + + + Float + + Float + + + + + + + Timeoff + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Timeoff + + + + + Float + + Float + + + + + + + Timeon + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSHeatWaveSimpleShader + OBSHeatWaveSimpleShader + Get + + Get-OBSHeatWaveSimpleShader [[-Rate] <float>] [[-Strength] <float>] [[-Distortion] <float>] [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHeatWaveSimpleShader + + Rate + + + + + Float + + Float + + + + + + + Strength + + + + + Float + + Float + + + + + + + Distortion + + + + + Float + + Float + + + + + + + Opacity + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + Distortion + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Opacity + + + + + Float + + Float + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + Rate + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Strength + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSHexagonShader + OBSHexagonShader + Get + + Get-OBSHexagonShader [[-HexColor] <string>] [[-AlphaPercent] <int>] [[-Quantity] <float>] [[-BorderWidth] <int>] [[-SpeedPercent] <int>] [[-DistortX] <float>] [[-DistortY] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Blend] [-Equilateral] [-ZoomAnimate] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHexagonShader + + HexColor + + + + + String + + String + + + + + + + AlphaPercent + + + + + Int + + Int + + + + + + + Quantity + + + + + Float + + Float + + + + + + + BorderWidth + + + + + Int + + Int + + + + + + + Blend + + + + + Switch + + Switch + + + + + + + Equilateral + + + + + Switch + + Switch + + + + + + + ZoomAnimate + + + + + Switch + + Switch + + + + + + + SpeedPercent + + + + + Int + + Int + + + + + + + Glitch + + + + + Switch + + Switch + + + + + + + DistortX + + + + + Float + + Float + + + + + + + DistortY + + + + + Float + + Float + + + + + + + OffsetX + + + + + Float + + Float + + + + + + + OffsetY + + + + + Float + + Float + + + + + + + Notes + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + AlphaPercent + + + + + Int + + Int + + + + + + + Blend + + + + + Switch + + Switch + + + + + + + BorderWidth + + + + + Int + + Int + + + + + + + DistortX + + + + + Float + + Float + + + + + + + DistortY + + + + + Float + + Float + + + + + + + Equilateral + + + + + Switch + + Switch + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Glitch + + + + + Switch + + Switch + + + + + + + HexColor + + + + + String + + String + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + Notes + + + + + String + + String + + + + + + + OffsetX + + + + + Float + + Float + + + + + + + OffsetY + + + + + Float + + Float + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + Quantity + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + SpeedPercent + + + + + Int + + Int + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + ZoomAnimate + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSHotkey + OBSHotkey + Get + + Get-OBSHotkey : GetHotkeyList + + 0.2.0.1 + + + Gets an array of all hotkey names in OBS. + Note: Hotkey functionality in obs-websocket comes as-is, and we do not guarantee support if things are broken. In 9/10 usages of hotkey requests, there exists a better, more reliable method via other requests. + Get-OBSHotkey calls the OBS WebSocket with a request of type GetHotkeyList. + + + + Get-OBSHotkey + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSHotkey + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#gethotkeylist + + + + + + Get-OBSHslHsvSaturationShader + OBSHslHsvSaturationShader + Get + + Get-OBSHslHsvSaturationShader [[-HslSaturationFactor] <float>] [[-HslGamma] <float>] [[-HsvSaturationFactor] <float>] [[-HsvGamma] <float>] [[-AdjustmentOrder] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHslHsvSaturationShader + + HslSaturationFactor + + + + + Float + + Float + + + + + + + HslGamma + + + + + Float + + Float + + + + + + + HsvSaturationFactor + + + + + Float + + Float + + + + + + + HsvGamma + + + + + Float + + Float + + + + + + + AdjustmentOrder + + + + + Int + + Int + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + AdjustmentOrder + + + + + Int + + Int + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + HslGamma + + + + + Float + + Float + + + + + + + HslSaturationFactor + + + + + Float + + Float + + + + + + + HsvGamma + + + + + Float + + Float + + + + + + + HsvSaturationFactor + + + + + Float + + Float + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSHueRotatonShader + OBSHueRotatonShader + Get + + Get-OBSHueRotatonShader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HueOverride] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSHueRotatonShader + + Speed + + + + + Float + + Float + + + + + + + HueOverride + + + + + Switch + + Switch + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + HueOverride + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + Speed + + + + + Float + + Float + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSInput + OBSInput + Get + + Get-OBSInput : GetInputList + + 0.2.0.1 + + + Gets an array of all inputs in OBS. + Get-OBSInput calls the OBS WebSocket with a request of type GetInputList. + + + + Get-OBSInput + + InputKind + + Restrict the array to only inputs of the specified kind + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputKind + + Restrict the array to only inputs of the specified kind + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputlist + + + + + + Get-OBSInputAudioBalance + OBSInputAudioBalance + Get + + Get-OBSInputAudioBalance : GetInputAudioBalance + + 0.2.0.1 + + + Gets the audio balance of an input. + Get-OBSInputAudioBalance calls the OBS WebSocket with a request of type GetInputAudioBalance. + + + + Get-OBSInputAudioBalance + + InputName + + Name of the input to get the audio balance of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio balance of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input to get the audio balance of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio balance of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiobalance + + + + + + Get-OBSInputAudioMonitorType + OBSInputAudioMonitorType + Get + + Get-OBSInputAudioMonitorType : GetInputAudioMonitorType + + 0.2.0.1 + + + Gets the audio monitor type of an input. + The available audio monitor types are: + - `OBS_MONITORING_TYPE_NONE` + - `OBS_MONITORING_TYPE_MONITOR_ONLY` + - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT` + Get-OBSInputAudioMonitorType calls the OBS WebSocket with a request of type GetInputAudioMonitorType. + + + + Get-OBSInputAudioMonitorType + + InputName + + Name of the input to get the audio monitor type of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio monitor type of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input to get the audio monitor type of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio monitor type of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiomonitortype + + + + + + Get-OBSInputAudioSyncOffset + OBSInputAudioSyncOffset + Get + + Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset + + 0.2.0.1 + + + Gets the audio sync offset of an input. + Note: The audio sync offset can be negative too! + Get-OBSInputAudioSyncOffset calls the OBS WebSocket with a request of type GetInputAudioSyncOffset. + + + + Get-OBSInputAudioSyncOffset + + InputName + + Name of the input to get the audio sync offset of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio sync offset of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input to get the audio sync offset of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the audio sync offset of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiosyncoffset + + + + + + Get-OBSInputAudioTracks + OBSInputAudioTracks + Get + + Get-OBSInputAudioTracks : GetInputAudioTracks + + 0.2.0.1 + + + Gets the enable state of all audio tracks of an input. + Get-OBSInputAudioTracks calls the OBS WebSocket with a request of type GetInputAudioTracks. + + + + Get-OBSInputAudioTracks + + InputName + + Name of the input + + String + + String + + + + + + + InputUuid + + UUID of the input + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input + + String + + String + + + + + + + InputUuid + + UUID of the input + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiotracks + + + + + + Get-OBSInputDefaultSettings + OBSInputDefaultSettings + Get + + Get-OBSInputDefaultSettings : GetInputDefaultSettings + + 0.2.0.1 + + + Gets the default settings for an input kind. + Get-OBSInputDefaultSettings calls the OBS WebSocket with a request of type GetInputDefaultSettings. + + + + Get-OBSInputDefaultSettings + + InputKind + + Input kind to get the default settings for + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputKind + + Input kind to get the default settings for + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputdefaultsettings + + + + + + Get-OBSInputKind + OBSInputKind + Get + + Get-OBSInputKind : GetInputKindList + + 0.2.0.1 + + + Gets an array of all available input kinds in OBS. + Get-OBSInputKind calls the OBS WebSocket with a request of type GetInputKindList. + + + + Get-OBSInputKind + + Unversioned + + True == Return all kinds as unversioned, False == Return with version suffixes (if available) + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + Unversioned + + True == Return all kinds as unversioned, False == Return with version suffixes (if available) + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputkindlist + + + + + + Get-OBSInputMute + OBSInputMute + Get + + Get-OBSInputMute : GetInputMute + + 0.2.0.1 + + + Gets the audio mute state of an input. + Get-OBSInputMute calls the OBS WebSocket with a request of type GetInputMute. + + + + Get-OBSInputMute + + InputName + + Name of input to get the mute state of + + String + + String + + + + + + + InputUuid + + UUID of input to get the mute state of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of input to get the mute state of + + String + + String + + + + + + + InputUuid + + UUID of input to get the mute state of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputmute + + + + + + Get-OBSInputPropertiesListPropertyItems + OBSInputPropertiesListPropertyItems + Get + + Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems + + 0.2.0.1 + + + Gets the items of a list property from an input's properties. + Note: Use this in cases where an input provides a dynamic, selectable list of items. For example, display capture, where it provides a list of available displays. + Get-OBSInputPropertiesListPropertyItems calls the OBS WebSocket with a request of type GetInputPropertiesListPropertyItems. + + + + Get-OBSInputPropertiesListPropertyItems + + InputName + + Name of the input + + String + + String + + + + + + + InputUuid + + UUID of the input + + String + + String + + + + + + + PropertyName + + Name of the list property to get the items of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input + + String + + String + + + + + + + InputUuid + + UUID of the input + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + PropertyName + + Name of the list property to get the items of + + String + + String + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputpropertieslistpropertyitems + + + + + + Get-OBSInputSettings + OBSInputSettings + Get + + Get-OBSInputSettings : GetInputSettings + + 0.2.0.1 + + + Gets the settings of an input. + Note: Does not include defaults. To create the entire settings object, overlay `inputSettings` over the `defaultInputSettings` provided by `GetInputDefaultSettings`. + Get-OBSInputSettings calls the OBS WebSocket with a request of type GetInputSettings. + + + + Get-OBSInputSettings + + InputName + + Name of the input to get the settings of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the settings of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input to get the settings of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the settings of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputsettings + + + + + + Get-OBSInputVolume + OBSInputVolume + Get + + Get-OBSInputVolume : GetInputVolume + + 0.2.0.1 + + + Gets the current volume setting of an input. + Get-OBSInputVolume calls the OBS WebSocket with a request of type GetInputVolume. + + + + Get-OBSInputVolume + + InputName + + Name of the input to get the volume of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the volume of + + String + + String + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + + + InputName + + Name of the input to get the volume of + + String + + String + + + + + + + InputUuid + + UUID of the input to get the volume of + + String + + String + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputvolume + + + + + + Get-OBSIntensityScopeShader + OBSIntensityScopeShader + Get + + Get-OBSIntensityScopeShader [[-Gain] <float>] [[-Blend] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSIntensityScopeShader + + Gain + + + + + Float + + Float + + + + + + + Blend + + + + + Float + + Float + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + Blend + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Gain + + + + + Float + + Float + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSInvertLumaShader + OBSInvertLumaShader + Get + + Get-OBSInvertLumaShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertColor] [-InvertLuma] [-GammaCorrection] [-TestRamp] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSInvertLumaShader + + InvertColor + + + + + Switch + + Switch + + + + + + + InvertLuma + + + + + Switch + + Switch + + + + + + + GammaCorrection + + + + + Switch + + Switch + + + + + + + TestRamp + + + + + Switch + + Switch + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + NoResponse - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch @@ -39318,9 +43358,10 @@ This can increase performance, and also silently ignore critical errors - InputName + FilterName - Name of the input + + String @@ -39331,13 +43372,56 @@ This can increase performance, and also silently ignore critical errors - InputUuid + Force - UUID of the input + + - String + Switch - String + Switch + + + + + + + GammaCorrection + + + + + Switch + + Switch + + + + + + + InvertColor + + + + + Switch + + Switch + + + + + + + InvertLuma + + + + + Switch + + Switch @@ -39346,8 +43430,8 @@ This can increase performance, and also silently ignore critical errors NoResponse - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39360,7 +43444,8 @@ This can increase performance, and also silently ignore critical errors PassThru - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39370,79 +43455,11 @@ This can increase performance, and also silently ignore critical errors - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputaudiotracks - - - - - - Get-OBSInputDefaultSettings - OBSInputDefaultSettings - Get - - Get-OBSInputDefaultSettings : GetInputDefaultSettings - - 0.2.0.1 - - - Gets the default settings for an input kind. - Get-OBSInputDefaultSettings calls the OBS WebSocket with a request of type GetInputDefaultSettings. - - - - Get-OBSInputDefaultSettings - - InputKind - - Input kind to get the default settings for - - String - - String - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - InputKind + ShaderText - Input kind to get the default settings for + + String @@ -39453,10 +43470,24 @@ This can increase performance, and also silently ignore critical errors - NoResponse + SourceName - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + + + String + + String + + + + + + + TestRamp + + + Switch @@ -39467,9 +43498,10 @@ This can increase performance, and also silently ignore critical errors - PassThru + UseShaderTime - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39480,44 +43512,42 @@ This can increase performance, and also silently ignore critical errors - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputdefaultsettings - - + + + + System.String + + + + + + + + + System.Object + + + + + - Get-OBSInputKind - OBSInputKind + Get-OBSLastReplayBufferReplay + OBSLastReplayBufferReplay Get - Get-OBSInputKind : GetInputKindList + Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay 0.2.0.1 - Gets an array of all available input kinds in OBS. - Get-OBSInputKind calls the OBS WebSocket with a request of type GetInputKindList. + Gets the filename of the last replay buffer save file. + Get-OBSLastReplayBufferReplay calls the OBS WebSocket with a request of type GetLastReplayBufferReplay. - Get-OBSInputKind - - Unversioned - - True == Return all kinds as unversioned, False == Return with version suffixes (if available) - - Switch - - Switch - - - - - + Get-OBSLastReplayBufferReplay PassThru @@ -39575,49 +43605,46 @@ This can increase performance, and also silently ignore critical errors - - Unversioned - - True == Return all kinds as unversioned, False == Return with version suffixes (if available) - - Switch - - Switch - - - - - + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSLastReplayBufferReplay + + + + - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputkindlist + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getlastreplaybufferreplay - Get-OBSInputMute - OBSInputMute + Get-OBSLuminance2Shader + OBSLuminance2Shader Get - Get-OBSInputMute : GetInputMute + Get-OBSLuminance2Shader [[-Color] <string>] [[-LumaMax] <float>] [[-LumaMin] <float>] [[-LumaMaxSmooth] <float>] [[-LumaMinSmooth] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets the audio mute state of an input. - Get-OBSInputMute calls the OBS WebSocket with a request of type GetInputMute. - Get-OBSInputMute - - InputName + Get-OBSLuminance2Shader + + Color - Name of input to get the mute state of + + String @@ -39627,23 +43654,67 @@ This can increase performance, and also silently ignore critical errors - - InputUuid + + LumaMax - UUID of input to get the mute state of + + - String + Float - String + Float - - PassThru + + LumaMin - If set, will return the information that would otherwise be sent to OBS. + + + + Float + + Float + + + + + + + LumaMaxSmooth + + + + + Float + + Float + + + + + + + LumaMinSmooth + + + + + Float + + Float + + + + + + + InvertImageColor + + + Switch @@ -39653,11 +43724,11 @@ This can increase performance, and also silently ignore critical errors - - NoResponse + + InvertAlphaChannel - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39667,93 +43738,53 @@ This can increase performance, and also silently ignore critical errors - - - - - InputName - - Name of input to get the mute state of - - String - - String - - - - - - - InputUuid - - UUID of input to get the mute state of - - String - - String - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputmute - - - - - - Get-OBSInputPropertiesListPropertyItems - OBSInputPropertiesListPropertyItems - Get - - Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems - - 0.2.0.1 - - - Gets the items of a list property from an input's properties. - Note: Use this in cases where an input provides a dynamic, selectable list of items. For example, display capture, where it provides a list of available displays. - Get-OBSInputPropertiesListPropertyItems calls the OBS WebSocket with a request of type GetInputPropertiesListPropertyItems. - - - - Get-OBSInputPropertiesListPropertyItems - - InputName + + Notes + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText - Name of the input + + String @@ -39763,36 +43794,39 @@ This can increase performance, and also silently ignore critical errors - - InputUuid + + Force - UUID of the input + + - String + Switch - String + Switch - - PropertyName + + PassThru - Name of the list property to get the items of + + - String + Switch - String + Switch - - PassThru + + NoResponse - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39802,11 +43836,11 @@ This can increase performance, and also silently ignore critical errors - - NoResponse + + UseShaderTime - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39820,9 +43854,10 @@ This can increase performance, and also silently ignore critical errors - InputName + Color - Name of the input + + String @@ -39833,9 +43868,10 @@ This can increase performance, and also silently ignore critical errors - InputUuid + FilterName - UUID of the input + + String @@ -39846,10 +43882,10 @@ This can increase performance, and also silently ignore critical errors - NoResponse + Force - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + Switch @@ -39860,9 +43896,10 @@ This can increase performance, and also silently ignore critical errors - PassThru + InvertAlphaChannel - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -39873,145 +43910,80 @@ This can increase performance, and also silently ignore critical errors - PropertyName + InvertImageColor - Name of the list property to get the items of + + - String + Switch - String + Switch - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputpropertieslistpropertyitems - - - - - - Get-OBSInputSettings - OBSInputSettings - Get - - Get-OBSInputSettings : GetInputSettings - - 0.2.0.1 - - - Gets the settings of an input. - Note: Does not include defaults. To create the entire settings object, overlay `inputSettings` over the `defaultInputSettings` provided by `GetInputDefaultSettings`. - Get-OBSInputSettings calls the OBS WebSocket with a request of type GetInputSettings. - - - - Get-OBSInputSettings - - InputName - - Name of the input to get the settings of - - String - - String - - - - - - - InputUuid - - UUID of the input to get the settings of - - String - - String - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - InputName + LumaMax - Name of the input to get the settings of + + - String + Float - String + Float - InputUuid + LumaMaxSmooth - UUID of the input to get the settings of + + - String + Float - String + Float - NoResponse + LumaMin - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + Float - Switch + Float - PassThru + LumaMinSmooth - If set, will return the information that would otherwise be sent to OBS. + + + + Float + + Float + + + + + + + NoResponse + + + Switch @@ -40021,92 +43993,11 @@ This can increase performance, and also silently ignore critical errors - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputsettings - - - - - - Get-OBSInputVolume - OBSInputVolume - Get - - Get-OBSInputVolume : GetInputVolume - - 0.2.0.1 - - - Gets the current volume setting of an input. - Get-OBSInputVolume calls the OBS WebSocket with a request of type GetInputVolume. - - - - Get-OBSInputVolume - - InputName - - Name of the input to get the volume of - - String - - String - - - - - - - InputUuid - - UUID of the input to get the volume of - - String - - String - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - InputName + Notes - Name of the input to get the volume of + + String @@ -40117,9 +44008,24 @@ This can increase performance, and also silently ignore critical errors - InputUuid + PassThru - UUID of the input to get the volume of + + + + Switch + + Switch + + + + + + + ShaderText + + + String @@ -40130,23 +44036,24 @@ This can increase performance, and also silently ignore critical errors - NoResponse + SourceName - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + String - Switch + String - PassThru + UseShaderTime - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -40157,21 +44064,32 @@ This can increase performance, and also silently ignore critical errors - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputvolume - - + + + + System.String + + + + + + + + + System.Object + + + + + - Get-OBSIntensityScopeShader - OBSIntensityScopeShader + Get-OBSLuminanceAlphaShader + OBSLuminanceAlphaShader Get - Get-OBSIntensityScopeShader [[-Gain] <float>] [[-Blend] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSLuminanceAlphaShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ColorMatrix] <float[][]>] [[-Color] <string>] [[-MulVal] <float>] [[-AddVal] <float>] [[-Level] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -40179,9 +44097,163 @@ This can increase performance, and also silently ignore critical errors - Get-OBSIntensityScopeShader + Get-OBSLuminanceAlphaShader - Gain + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + Image + + + + + String + + String + + + + + + + ElapsedTime + + + + + Float + + Float + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + RandF + + + + + Float + + Float + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ColorMatrix + + + + + System.Single[][] + + System.Single[][] + + + + + + + Color + + + + + String + + String + + + + + + + MulVal + + + + + Float + + Float + + + + + + + AddVal @@ -40194,8 +44266,8 @@ This can increase performance, and also silently ignore critical errors - - Blend + + Level @@ -40208,7 +44280,21 @@ This can increase performance, and also silently ignore critical errors - + + InvertAlphaChannel + + + + + Switch + + Switch + + + + + + SourceName @@ -40222,7 +44308,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -40236,7 +44322,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -40310,7 +44396,7 @@ This can increase performance, and also silently ignore critical errors - Blend + AddVal @@ -40324,7 +44410,7 @@ This can increase performance, and also silently ignore critical errors - FilterName + Color @@ -40338,21 +44424,21 @@ This can increase performance, and also silently ignore critical errors - Force + ColorMatrix - Switch + System.Single[][] - Switch + System.Single[][] - Gain + ElapsedTime @@ -40366,21 +44452,21 @@ This can increase performance, and also silently ignore critical errors - NoResponse + FilterName - Switch + String - Switch + String - PassThru + Force @@ -40394,7 +44480,7 @@ This can increase performance, and also silently ignore critical errors - ShaderText + Image @@ -40408,241 +44494,63 @@ This can increase performance, and also silently ignore critical errors - SourceName + InvertAlphaChannel - String + Switch - String + Switch - UseShaderTime + Level - Switch + Float - Switch + Float - - - - - System.String - - - - - - - + + MulVal + + + + + Float - System.Object + Float - - - - - - - Get-OBSInvertLumaShader - OBSInvertLumaShader - Get - - Get-OBSInvertLumaShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertColor] [-InvertLuma] [-GammaCorrection] [-TestRamp] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSInvertLumaShader - - InvertColor - - - - - Switch - - Switch - - - - - - - InvertLuma - - - - - Switch - - Switch - - - - - - - GammaCorrection - - - - - Switch - - Switch - - - - - - - TestRamp - - - - - Switch - - Switch - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - ShaderText - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - PassThru - - - - - Switch - - Switch - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - UseShaderTime - - - - - Switch - - Switch - - - - - - - - + + + - FilterName + NoResponse - String + Switch - String + Switch - Force + PassThru @@ -40656,49 +44564,49 @@ This can increase performance, and also silently ignore critical errors - GammaCorrection + RandF - Switch + Float - Switch + Float - InvertColor + ShaderText - Switch + String - Switch + String - InvertLuma + SourceName - Switch + String - Switch + String - NoResponse + UseShaderTime @@ -40712,70 +44620,70 @@ This can increase performance, and also silently ignore critical errors - PassThru + UvOffset - Switch + System.Single[] - Switch + System.Single[] - ShaderText + UvPixelInterval - String + System.Single[] - String + System.Single[] - SourceName + UvScale - String + System.Single[] - String + System.Single[] - TestRamp + UvSize - Switch + System.Single[] - Switch + System.Single[] - UseShaderTime + ViewProj - Switch + System.Single[][] - Switch + System.Single[][] @@ -40803,105 +44711,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSLastReplayBufferReplay - OBSLastReplayBufferReplay - Get - - Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay - - 0.2.0.1 - - - Gets the filename of the last replay buffer save file. - Get-OBSLastReplayBufferReplay calls the OBS WebSocket with a request of type GetLastReplayBufferReplay. - - - - Get-OBSLastReplayBufferReplay - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSLastReplayBufferReplay - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getlastreplaybufferreplay - - - - - - Get-OBSLuminance2Shader - OBSLuminance2Shader + Get-OBSLuminanceShader + OBSLuminanceShader Get - Get-OBSLuminance2Shader [[-Color] <string>] [[-LumaMax] <float>] [[-LumaMin] <float>] [[-LumaMaxSmooth] <float>] [[-LumaMinSmooth] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSLuminanceShader [[-Color] <string>] [[-Level] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -40909,7 +44723,7 @@ This can increase performance, and also silently ignore critical errors - Get-OBSLuminance2Shader + Get-OBSLuminanceShader Color @@ -40925,49 +44739,7 @@ This can increase performance, and also silently ignore critical errors - LumaMax - - - - - Float - - Float - - - - - - - LumaMin - - - - - Float - - Float - - - - - - - LumaMaxSmooth - - - - - Float - - Float - - - - - - - LumaMinSmooth + Level @@ -41008,7 +44780,7 @@ This can increase performance, and also silently ignore critical errors - + Notes @@ -41022,7 +44794,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -41036,7 +44808,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -41050,7 +44822,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -41131,112 +44903,70 @@ This can increase performance, and also silently ignore critical errors String - String - - - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - InvertAlphaChannel - - - - - Switch - - Switch + String - InvertImageColor + FilterName - Switch + String - Switch + String - LumaMax + Force - Float + Switch - Float + Switch - LumaMaxSmooth + InvertAlphaChannel - Float + Switch - Float + Switch - LumaMin + InvertImageColor - Float + Switch - Float + Switch - LumaMinSmooth + Level @@ -41355,11 +45085,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSLuminanceAlphaShader - OBSLuminanceAlphaShader + Get-OBSMatrixShader + OBSMatrixShader Get - Get-OBSLuminanceAlphaShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ColorMatrix] <float[][]>] [[-Color] <string>] [[-MulVal] <float>] [[-AddVal] <float>] [[-Level] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSMatrixShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Mouse] <float[]>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-Ratio] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertDirection] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -41367,7 +45097,7 @@ This can increase performance, and also silently ignore critical errors - Get-OBSLuminanceAlphaShader + Get-OBSMatrixShader ViewProj @@ -41439,7 +45169,7 @@ This can increase performance, and also silently ignore critical errors - UvPixelInterval + UvSize @@ -41453,6 +45183,20 @@ This can increase performance, and also silently ignore critical errors + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + RandF @@ -41466,8 +45210,64 @@ This can increase performance, and also silently ignore critical errors - - UvSize + + RandInstanceF + + + + + Float + + Float + + + + + + + RandActivationF + + + + + Float + + Float + + + + + + + Loops + + + + + Int + + Int + + + + + + + LocalTime + + + + + Float + + Float + + + + + + + Mouse @@ -41480,36 +45280,36 @@ This can increase performance, and also silently ignore critical errors - - ColorMatrix + + InvertDirection - System.Single[][] + Switch - System.Single[][] + Switch - - Color + + LumaMin - String + Float - String + Float - - MulVal + + LumaMinSmooth @@ -41522,8 +45322,8 @@ This can increase performance, and also silently ignore critical errors - - AddVal + + Ratio @@ -41536,8 +45336,8 @@ This can increase performance, and also silently ignore critical errors - - Level + + AlphaPercentage @@ -41551,7 +45351,7 @@ This can increase performance, and also silently ignore critical errors - InvertAlphaChannel + ApplyToAlphaLayer @@ -41564,7 +45364,7 @@ This can increase performance, and also silently ignore critical errors - + SourceName @@ -41578,7 +45378,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -41592,7 +45392,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -41666,7 +45466,7 @@ This can increase performance, and also silently ignore critical errors - AddVal + AlphaPercentage @@ -41680,49 +45480,63 @@ This can increase performance, and also silently ignore critical errors - Color + ApplyToAlphaLayer - String + Switch - String + Switch - ColorMatrix + ElapsedTime - System.Single[][] + Float - System.Single[][] + Float - ElapsedTime + FilterName - Float + String - Float + String - FilterName + Force + + + + + Switch + + Switch + + + + + + + Image @@ -41736,7 +45550,7 @@ This can increase performance, and also silently ignore critical errors - Force + InvertDirection @@ -41750,35 +45564,35 @@ This can increase performance, and also silently ignore critical errors - Image + LocalTime - String + Float - String + Float - InvertAlphaChannel + Loops - Switch + Int - Switch + Int - Level + LumaMin @@ -41792,7 +45606,7 @@ This can increase performance, and also silently ignore critical errors - MulVal + LumaMinSmooth @@ -41805,6 +45619,20 @@ This can increase performance, and also silently ignore critical errors + + Mouse + + + + + System.Single[] + + System.Single[] + + + + + NoResponse @@ -41833,6 +45661,20 @@ This can increase performance, and also silently ignore critical errors + + RandActivationF + + + + + Float + + Float + + + + + RandF @@ -41847,6 +45689,34 @@ This can increase performance, and also silently ignore critical errors + + RandInstanceF + + + + + Float + + Float + + + + + + + Ratio + + + + + Float + + Float + + + + + ShaderText @@ -41981,108 +45851,34 @@ This can increase performance, and also silently ignore critical errors - Get-OBSLuminanceShader - OBSLuminanceShader + Get-OBSMediaInputStatus + OBSMediaInputStatus Get - Get-OBSLuminanceShader [[-Color] <string>] [[-Level] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSMediaInputStatus : GetMediaInputStatus 0.2.0.1 + Gets the status of a media input. + Media States: + - `OBS_MEDIA_STATE_NONE` + - `OBS_MEDIA_STATE_PLAYING` + - `OBS_MEDIA_STATE_OPENING` + - `OBS_MEDIA_STATE_BUFFERING` + - `OBS_MEDIA_STATE_PAUSED` + - `OBS_MEDIA_STATE_STOPPED` + - `OBS_MEDIA_STATE_ENDED` + - `OBS_MEDIA_STATE_ERROR` + Get-OBSMediaInputStatus calls the OBS WebSocket with a request of type GetMediaInputStatus. - Get-OBSLuminanceShader - - Color - - - - - String - - String - - - - - - - Level - - - - - Float - - Float - - - - - - - InvertImageColor - - - - - Switch - - Switch - - - - - - - InvertAlphaChannel - - - - - Switch - - Switch - - - - - - - Notes - - - - - String - - String - - - - - - - SourceName - - - - - String - - String - - - - - - - FilterName + Get-OBSMediaInputStatus + + InputName - - + Name of the media input String @@ -42092,11 +45888,10 @@ This can increase performance, and also silently ignore critical errors - - ShaderText + + InputUuid - - + UUID of the media input String @@ -42106,25 +45901,10 @@ This can increase performance, and also silently ignore critical errors - - Force - - - - - Switch - - Switch - - - - - - + PassThru - - + If set, will return the information that would otherwise be sent to OBS. Switch @@ -42134,25 +45914,11 @@ This can increase performance, and also silently ignore critical errors - + NoResponse - - - - Switch - - Switch - - - - - - - UseShaderTime - - - + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors Switch @@ -42166,108 +45932,9 @@ This can increase performance, and also silently ignore critical errors - Color - - - - - String - - String - - - - - - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - - - InvertAlphaChannel - - - - - Switch - - Switch - - - - - - - InvertImageColor - - - - - Switch - - Switch - - - - - - - Level - - - - - Float - - Float - - - - - - - NoResponse - - - - - Switch - - Switch - - - - - - - Notes + InputName - - + Name of the media input String @@ -42278,24 +45945,9 @@ This can increase performance, and also silently ignore critical errors - PassThru - - - - - Switch - - Switch - - - - - - - ShaderText + InputUuid - - + UUID of the media input String @@ -42306,24 +45958,23 @@ This can increase performance, and also silently ignore critical errors - SourceName + NoResponse - - + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors - String + Switch - String + Switch - UseShaderTime + PassThru - - + If set, will return the information that would otherwise be sent to OBS. Switch @@ -42334,227 +45985,35 @@ This can increase performance, and also silently ignore critical errors - - - - System.String - - - - - - - - - System.Object - - - - - + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getmediainputstatus + + - Get-OBSMatrixShader - OBSMatrixShader + Get-OBSMonitor + OBSMonitor Get - Get-OBSMatrixShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Mouse] <float[]>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-Ratio] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertDirection] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSMonitor : GetMonitorList 0.2.0.1 + Gets a list of connected monitors and information about them. + Get-OBSMonitor calls the OBS WebSocket with a request of type GetMonitorList. - Get-OBSMatrixShader - - ViewProj - - - - - System.Single[][] - - System.Single[][] - - - - - - - Image - - - - - String - - String - - - - - - - ElapsedTime - - - - - Float - - Float - - - - - - - UvOffset - - - - - System.Single[] - - System.Single[] - - - - - - - UvScale - - - - - System.Single[] - - System.Single[] - - - - - - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - UvPixelInterval - - - - - System.Single[] - - System.Single[] - - - - - - - RandF - - - - - Float - - Float - - - - - - - RandInstanceF - - - - - Float - - Float - - - - - - - RandActivationF - - - - - Float - - Float - - - - - - - Loops - - - - - Int - - Int - - - - - - - LocalTime - - - - - Float - - Float - - - - - - - Mouse - - - - - System.Single[] - - System.Single[] - - - - - - - InvertDirection + Get-OBSMonitor + + PassThru - - + If set, will return the information that would otherwise be sent to OBS. Switch @@ -42564,50 +46023,101 @@ This can increase performance, and also silently ignore critical errors - - LumaMin - - - - - Float - - Float - - - - - - - LumaMinSmooth + + NoResponse - - + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors - Float + Switch - Float + Switch - - Ratio + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSMonitor + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getmonitorlist + + + + + + Get-OBSMotionBlurShader + OBSMotionBlurShader + Get + + Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSMotionBlurShader + + PreviousOutput - Float + String - Float + String - - AlphaPercentage + + Strength @@ -42620,21 +46130,7 @@ This can increase performance, and also silently ignore critical errors - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - + SourceName @@ -42648,7 +46144,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -42662,7 +46158,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -42735,48 +46231,6 @@ This can increase performance, and also silently ignore critical errors - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToAlphaLayer - - - - - Switch - - Switch - - - - - - - ElapsedTime - - - - - Float - - Float - - - - - FilterName @@ -42797,114 +46251,30 @@ This can increase performance, and also silently ignore critical errors - Switch - - Switch - - - - - - - Image - - - - - String - - String - - - - - - - InvertDirection - - - - - Switch - - Switch - - - - - - - LocalTime - - - - - Float - - Float - - - - - - - Loops - - - - - Int - - Int - - - - - - - LumaMin - - - - - Float - - Float - - - - - - - LumaMinSmooth - - - - - Float + Switch - Float + Switch - Mouse + NoResponse - System.Single[] + Switch - System.Single[] + Switch - NoResponse + PassThru @@ -42918,49 +46288,49 @@ This can increase performance, and also silently ignore critical errors - PassThru + PreviousOutput - Switch + String - Switch + String - RandActivationF + ShaderText - Float + String - Float + String - RandF + SourceName - Float + String - Float + String - RandInstanceF + Strength @@ -42974,21 +46344,171 @@ This can increase performance, and also silently ignore critical errors - Ratio + UseShaderTime - Float + Switch - Float + Switch + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSMultiplyShader + OBSMultiplyShader + Get + + Get-OBSMultiplyShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSMultiplyShader + + OtherImage + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + - ShaderText + FilterName @@ -43002,21 +46522,21 @@ This can increase performance, and also silently ignore critical errors - SourceName + Force - String + Switch - String + Switch - UseShaderTime + NoResponse @@ -43030,70 +46550,70 @@ This can increase performance, and also silently ignore critical errors - UvOffset + OtherImage - System.Single[] + String - System.Single[] + String - UvPixelInterval + PassThru - System.Single[] + Switch - System.Single[] + Switch - UvScale + ShaderText - System.Single[] + String - System.Single[] + String - UvSize + SourceName - System.Single[] + String - System.Single[] + String - ViewProj + UseShaderTime - System.Single[][] + Switch - System.Single[][] + Switch @@ -43121,60 +46641,52 @@ This can increase performance, and also silently ignore critical errors - Get-OBSMediaInputStatus - OBSMediaInputStatus + Get-OBSNightSkyShader + OBSNightSkyShader Get - Get-OBSMediaInputStatus : GetMediaInputStatus + Get-OBSNightSkyShader [[-Speed] <float>] [[-CenterWidthPercentage] <int>] [[-CenterHeightPercentage] <int>] [[-AlphaPercentage] <float>] [[-NumberStars] <int>] [[-SKYCOLOR] <string>] [[-STARCOLOR] <string>] [[-LIGHTSKY] <string>] [[-SKYLIGHTNESS] <float>] [[-MOONCOLOR] <string>] [[-MoonSize] <float>] [[-MoonBumpSize] <float>] [[-MoonPositionX] <float>] [[-MoonPositionY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IncludeClouds] [-IncludeMoon] [-ApplyToImage] [-ReplaceImageColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets the status of a media input. - Media States: - - `OBS_MEDIA_STATE_NONE` - - `OBS_MEDIA_STATE_PLAYING` - - `OBS_MEDIA_STATE_OPENING` - - `OBS_MEDIA_STATE_BUFFERING` - - `OBS_MEDIA_STATE_PAUSED` - - `OBS_MEDIA_STATE_STOPPED` - - `OBS_MEDIA_STATE_ENDED` - - `OBS_MEDIA_STATE_ERROR` - Get-OBSMediaInputStatus calls the OBS WebSocket with a request of type GetMediaInputStatus. - Get-OBSMediaInputStatus - - InputName + Get-OBSNightSkyShader + + Speed - Name of the media input + + - String + Float - String + Float - - InputUuid + + IncludeClouds - UUID of the media input + + - String + Switch - String + Switch - - PassThru + + IncludeMoon - If set, will return the information that would otherwise be sent to OBS. + + Switch @@ -43184,11 +46696,53 @@ This can increase performance, and also silently ignore critical errors - - NoResponse + + CenterWidthPercentage - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + + + Int + + Int + + + + + + + CenterHeightPercentage + + + + + Int + + Int + + + + + + + AlphaPercentage + + + + + Float + + Float + + + + + + + ApplyToImage + + + Switch @@ -43198,197 +46752,151 @@ This can increase performance, and also silently ignore critical errors - - - - - InputName - - Name of the media input - - String - - String - - - - - - - InputUuid - - UUID of the media input - - String - - String - - - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getmediainputstatus - - - - - - Get-OBSMonitor - OBSMonitor - Get - - Get-OBSMonitor : GetMonitorList - - 0.2.0.1 - - - Gets a list of connected monitors and information about them. - Get-OBSMonitor calls the OBS WebSocket with a request of type GetMonitorList. - - - - Get-OBSMonitor - - PassThru + + ReplaceImageColor - If set, will return the information that would otherwise be sent to OBS. + + Switch - Switch + Switch + + + + + + + NumberStars + + + + + Int + + Int + + + + + + + SKYCOLOR + + + + + String + + String + + + + + + + STARCOLOR + + + + + String + + String + + + + + + + LIGHTSKY + + + + + String + + String + + + + + + + SKYLIGHTNESS + + + + + Float + + Float + + + + + + + MOONCOLOR + + + + + String + + String + + + + + + + MoonSize + + + + + Float + + Float - - NoResponse + + MoonBumpSize - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + - Switch + Float - Switch + Float - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSMonitor - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getmonitorlist - - - - - - Get-OBSMotionBlurShader - OBSMotionBlurShader - Get - - -Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - - 0.2.0.1 - - - - - - Get-OBSMotionBlurShader - - PreviousOutput + + MoonPositionX + + - String + Float - String + Float - - Strength + + MoonPositionY + + Float @@ -43398,9 +46906,11 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + SourceName + + String @@ -43410,9 +46920,11 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + FilterName + + String @@ -43422,9 +46934,11 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + ShaderText + + String @@ -43437,6 +46951,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force + + Switch @@ -43449,6 +46965,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa PassThru + + Switch @@ -43461,6 +46979,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa NoResponse + + Switch @@ -43473,6 +46993,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa UseShaderTime + + Switch @@ -43485,9 +47007,67 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + AlphaPercentage + + + + + Float + + Float + + + + + + + ApplyToImage + + + + + Switch + + Switch + + + + + + + CenterHeightPercentage + + + + + Int + + Int + + + + + + + CenterWidthPercentage + + + + + Int + + Int + + + + + FilterName + + String @@ -43500,6 +47080,36 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force + + + + Switch + + Switch + + + + + + + IncludeClouds + + + + + Switch + + Switch + + + + + + + IncludeMoon + + + Switch @@ -43509,9 +47119,95 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + LIGHTSKY + + + + + String + + String + + + + + + + MoonBumpSize + + + + + Float + + Float + + + + + + + MOONCOLOR + + + + + String + + String + + + + + + + MoonPositionX + + + + + Float + + Float + + + + + + + MoonPositionY + + + + + Float + + Float + + + + + + + MoonSize + + + + + Float + + Float + + + + + NoResponse + + Switch @@ -43521,9 +47217,25 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + NumberStars + + + + + Int + + Int + + + + + PassThru + + Switch @@ -43534,8 +47246,24 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - PreviousOutput + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + ShaderText + + String @@ -43546,8 +47274,10 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - ShaderText + SKYCOLOR + + String @@ -43557,9 +47287,25 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + SKYLIGHTNESS + + + + + Float + + Float + + + + + SourceName + + String @@ -43570,8 +47316,10 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Strength + Speed + + Float @@ -43581,9 +47329,25 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + STARCOLOR + + + + + String + + String + + + + + UseShaderTime + + Switch @@ -43615,11 +47379,13 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Get-OBSMultiplyShader - OBSMultiplyShader + Get-OBSNoiseShader + OBSNoiseShader Get - Get-OBSMultiplyShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLevel] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -43627,26 +47393,70 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Get-OBSMultiplyShader + Get-OBSNoiseShader - OtherImage + Speed - - - String + Float - String + Float - + + Scale + + + Float + + Float + + + + + + + NoiseLevel + + + Float + + Float + + + + + + + Monochromatic + + + Switch + + Switch + + + + + + + UseRand + + + Switch + + Switch + + + + + + SourceName - - String @@ -43656,11 +47466,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + FilterName - - String @@ -43670,11 +47478,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + ShaderText - - String @@ -43687,8 +47493,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force - - Switch @@ -43701,8 +47505,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa PassThru - - Switch @@ -43715,8 +47517,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa NoResponse - - Switch @@ -43729,8 +47529,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa UseShaderTime - - Switch @@ -43746,8 +47544,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa FilterName - - String @@ -43760,8 +47556,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force - - Switch @@ -43772,10 +47566,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - NoResponse + Monochromatic - - Switch @@ -43786,14 +47578,24 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - OtherImage + NoiseLevel - - - String + Float - String + Float + + + + + + + NoResponse + + + Switch + + Switch @@ -43802,8 +47604,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa PassThru - - Switch @@ -43813,11 +47613,21 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + Scale + + + Float + + Float + + + + + ShaderText - - String @@ -43830,8 +47640,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa SourceName - - String @@ -43841,11 +47649,33 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa + + Speed + + + Float + + Float + + + + + + + UseRand + + + Switch + + Switch + + + + + UseShaderTime - - Switch @@ -43877,11 +47707,13 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Get-OBSNightSkyShader - OBSNightSkyShader + Get-OBSNormalMapShader + OBSNormalMapShader Get - Get-OBSNightSkyShader [[-Speed] <float>] [[-CenterWidthPercentage] <int>] [[-CenterHeightPercentage] <int>] [[-AlphaPercentage] <float>] [[-NumberStars] <int>] [[-SKYCOLOR] <string>] [[-STARCOLOR] <string>] [[-LIGHTSKY] <string>] [[-SKYLIGHTNESS] <float>] [[-MOONCOLOR] <string>] [[-MoonSize] <float>] [[-MoonBumpSize] <float>] [[-MoonPositionX] <float>] [[-MoonPositionY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IncludeClouds] [-IncludeMoon] [-ApplyToImage] [-ReplaceImageColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -43889,12 +47721,10 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Get-OBSNightSkyShader + Get-OBSNormalMapShader - Speed + Strength - - Float @@ -43905,10 +47735,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - IncludeClouds + OffsetHeight - - Switch @@ -43919,10 +47747,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - IncludeMoon + InvertR - - Switch @@ -43932,53 +47758,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - - CenterWidthPercentage - - - - - Int - - Int - - - - - - - CenterHeightPercentage - - - - - Int - - Int - - - - - - - AlphaPercentage - - - - - Float - - Float - - - - - - ApplyToImage + InvertG - - Switch @@ -43989,10 +47771,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - ReplaceImageColor + InvertH - - Switch @@ -44002,11 +47782,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - - NumberStars + + Type - - Int @@ -44016,137 +47794,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - - SKYCOLOR - - - - - String - - String - - - - - - - STARCOLOR - - - - - String - - String - - - - - - - LIGHTSKY - - - - - String - - String - - - - - - - SKYLIGHTNESS - - - - - Float - - Float - - - - - - - MOONCOLOR - - - - - String - - String - - - - - - - MoonSize - - - - - Float - - Float - - - - - - - MoonBumpSize - - - - - Float - - Float - - - - - - - MoonPositionX - - - - - Float - - Float - - - - - - - MoonPositionY - - - - - Float - - Float - - - - - - + SourceName - - String @@ -44156,11 +47806,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + FilterName - - String @@ -44170,11 +47818,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - + ShaderText - - String @@ -44187,8 +47833,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force - - Switch @@ -44201,8 +47845,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa PassThru - - Switch @@ -44215,8 +47857,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa NoResponse - - Switch @@ -44229,8 +47869,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa UseShaderTime - - Switch @@ -44243,67 +47881,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - CenterHeightPercentage - - - - - Int - - Int - - - - - - - CenterWidthPercentage - - - - - Int - - Int - - - - - FilterName - - String @@ -44316,8 +47896,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa Force - - Switch @@ -44328,10 +47906,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - IncludeClouds + InvertG - - Switch @@ -44342,10 +47918,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - IncludeMoon + InvertH - - Switch @@ -44356,84 +47930,12 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - LIGHTSKY - - - - - String - - String - - - - - - - MoonBumpSize - - - - - Float - - Float - - - - - - - MOONCOLOR - - - - - String - - String - - - - - - - MoonPositionX - - - - - Float - - Float - - - - - - - MoonPositionY - - - - - Float - - Float - - - - - - - MoonSize + InvertR - - - Float + Switch - Float + Switch @@ -44442,8 +47944,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa NoResponse - - Switch @@ -44454,24 +47954,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - NumberStars - - - - - Int - - Int - - - - - - - PassThru + OffsetHeight - - Switch @@ -44482,10 +47966,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - ReplaceImageColor + PassThru - - Switch @@ -44498,22 +47980,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa ShaderText - - - - String - - String - - - - - - - SKYCOLOR - - - String @@ -44523,25 +47989,9 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - - SKYLIGHTNESS - - - - - Float - - Float - - - - - SourceName - - String @@ -44552,10 +48002,8 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - Speed + Strength - - Float @@ -44566,14 +48014,12 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa - STARCOLOR + Type - - - String + Int - String + Int @@ -44582,8 +48028,6 @@ Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <floa UseShaderTime - - Switch @@ -46562,9 +50006,7 @@ This can increase performance, and also silently ignore critical errorsOBSPerspectiveShader Get - -Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[-AngleZ] <float>] [[-Perspective] <float>] [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[-AngleZ] <float>] [[-Perspective] <float>] [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -46576,6 +50018,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleX + + Float @@ -46588,6 +50032,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleY + + Float @@ -46600,6 +50046,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleZ + + Float @@ -46612,6 +50060,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- Perspective + + Float @@ -46624,6 +50074,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- BorderColor + + String @@ -46636,6 +50088,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- ShowBorder + + Switch @@ -46648,6 +50102,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- SourceName + + String @@ -46660,6 +50116,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- FilterName + + String @@ -46672,6 +50130,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- ShaderText + + String @@ -46684,6 +50144,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- Force + + Switch @@ -46696,6 +50158,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- PassThru + + Switch @@ -46708,6 +50172,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- NoResponse + + Switch @@ -46720,6 +50186,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- UseShaderTime + + Switch @@ -46735,6 +50203,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleX + + Float @@ -46747,6 +50217,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleY + + Float @@ -46759,6 +50231,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- AngleZ + + Float @@ -46771,6 +50245,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- BorderColor + + String @@ -46783,6 +50259,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- FilterName + + String @@ -46795,6 +50273,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- Force + + Switch @@ -46807,6 +50287,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- NoResponse + + Switch @@ -46819,6 +50301,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- PassThru + + Switch @@ -46831,6 +50315,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- Perspective + + Float @@ -46843,6 +50329,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- ShaderText + + String @@ -46855,6 +50343,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- ShowBorder + + Switch @@ -46867,6 +50357,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- SourceName + + String @@ -46879,6 +50371,8 @@ Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[- UseShaderTime + + Switch @@ -49707,6 +53201,406 @@ This can increase performance, and also silently ignore critical errors + + + Get-OBSQuadrilateralCropShader + OBSQuadrilateralCropShader + Get + + +Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <float>] [[-TopRightX] <float>] [[-TopRightY] <float>] [[-BottomLeftX] <float>] [[-BottomLeftY] <float>] [[-BottomRightX] <float>] [[-BottomRightY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + + 0.2.0.1 + + + + + + Get-OBSQuadrilateralCropShader + + TopLeftX + + + Float + + Float + + + + + + + TopLeftY + + + Float + + Float + + + + + + + TopRightX + + + Float + + Float + + + + + + + TopRightY + + + Float + + Float + + + + + + + BottomLeftX + + + Float + + Float + + + + + + + BottomLeftY + + + Float + + Float + + + + + + + BottomRightX + + + Float + + Float + + + + + + + BottomRightY + + + Float + + Float + + + + + + + SourceName + + + String + + String + + + + + + + FilterName + + + String + + String + + + + + + + ShaderText + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + BottomLeftX + + + Float + + Float + + + + + + + BottomLeftY + + + Float + + Float + + + + + + + BottomRightX + + + Float + + Float + + + + + + + BottomRightY + + + Float + + Float + + + + + + + FilterName + + + String + + String + + + + + + + Force + + + Switch + + Switch + + + + + + + NoResponse + + + Switch + + Switch + + + + + + + PassThru + + + Switch + + Switch + + + + + + + ShaderText + + + String + + String + + + + + + + SourceName + + + String + + String + + + + + + + TopLeftX + + + Float + + Float + + + + + + + TopLeftY + + + Float + + Float + + + + + + + TopRightX + + + Float + + Float + + + + + + + TopRightY + + + Float + + Float + + + + + + + UseShaderTime + + + Switch + + Switch + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + Get-OBSRainbowShader @@ -51883,9 +55777,7 @@ This can increase performance, and also silently ignore critical errorsOBSRepeatGridCenterCropShader Get - -Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-Alpha] <float>] [[-Columns] <float>] [[-Rows] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-Alpha] <float>] [[-Columns] <float>] [[-Rows] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -51897,6 +55789,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < ViewProj + + System.Single[][] @@ -51909,6 +55803,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Image + + String @@ -51921,6 +55817,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Alpha + + Float @@ -51933,6 +55831,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Columns + + Float @@ -51945,6 +55845,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Rows + + Float @@ -51957,6 +55859,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < SourceName + + String @@ -51969,6 +55873,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < FilterName + + String @@ -51981,6 +55887,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < ShaderText + + String @@ -51993,6 +55901,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Force + + Switch @@ -52005,6 +55915,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < PassThru + + Switch @@ -52017,6 +55929,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < NoResponse + + Switch @@ -52029,6 +55943,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < UseShaderTime + + Switch @@ -52044,6 +55960,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Alpha + + Float @@ -52056,6 +55974,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Columns + + Float @@ -52068,6 +55988,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < FilterName + + String @@ -52080,6 +56002,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Force + + Switch @@ -52092,6 +56016,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Image + + String @@ -52104,6 +56030,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < NoResponse + + Switch @@ -52116,6 +56044,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < PassThru + + Switch @@ -52128,6 +56058,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < Rows + + Float @@ -52140,6 +56072,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < ShaderText + + String @@ -52152,6 +56086,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < SourceName + + String @@ -52164,6 +56100,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < UseShaderTime + + Switch @@ -52176,6 +56114,8 @@ Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] < ViewProj + + System.Single[][] @@ -70485,91 +74425,591 @@ This can increase performance, and also silently ignore critical errors - - UvSize - - - - - System.Single[] - - System.Single[] - - - - - - - ShadowOffsetX + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ShadowOffsetX + + + + + Int + + Int + + + + + + + ShadowOffsetY + + + + + Int + + Int + + + + + + + ShadowBlurSize + + + + + Int + + Int + + + + + + + ShadowColor + + + + + String + + String + + + + + + + IsAlphaPremultiplied + + + + + Switch + + Switch + + + + + + + SourceName + + + + + String + + String + + + + + + + FilterName + + + + + String + + String + + + + + + + ShaderText + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + + + + ElapsedTime + + + + + Float + + Float + + + + + + + FilterName + + + + + String + + String + + + + + + + Force + + + + + Switch + + Switch + + + + + + + Image + + + + + String + + String + + + + + + + IsAlphaPremultiplied + + + + + Switch + + Switch + + + + + + + NoResponse + + + + + Switch + + Switch + + + + + + + PassThru + + + + + Switch + + Switch + + + + + + + RandF + + + + + Float + + Float + + + + + + + ShaderText + + + + + String + + String + + + + + + + ShadowBlurSize + + + + + Int + + Int + + + + + + + ShadowColor + + + + + String + + String + + + + + + + ShadowOffsetX + + + + + Int + + Int + + + + + + + ShadowOffsetY + + + + + Int + + Int + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch + + + + + + + UvOffset + + + + + System.Single[] + + System.Single[] + + + + + + + UvPixelInterval + + + + + System.Single[] + + System.Single[] + + + + + + + UvScale + + + + + System.Single[] + + System.Single[] + + + + + + + UvSize + + + + + System.Single[] + + System.Single[] + + + + + + + ViewProj + + + + + System.Single[][] + + System.Single[][] + + + + + + + + + + System.String + + + + + + + + + System.Object + + + + + + + + + Get-OBSVCRShader + OBSVCRShader + Get + + Get-OBSVCRShader [[-VerticalShift] <float>] [[-Distort] <float>] [[-Vignet] <float>] [[-Stripe] <float>] [[-VerticalFactor] <float>] [[-VerticalHeight] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + + 0.2.0.1 + + + + + + Get-OBSVCRShader + + VerticalShift + + + + + Float + + Float + + + + + + + Distort - Int + Float - Int + Float - - ShadowOffsetY + + Vignet - Int + Float - Int + Float - - ShadowBlurSize + + Stripe - Int + Float - Int + Float - - ShadowColor + + VerticalFactor - String + Float - String + Float - - IsAlphaPremultiplied + + VerticalHeight - Switch + Float - Switch + Float - + SourceName @@ -70583,7 +75023,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -70597,7 +75037,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -70671,7 +75111,7 @@ This can increase performance, and also silently ignore critical errors - ElapsedTime + Distort @@ -70712,34 +75152,6 @@ This can increase performance, and also silently ignore critical errors - - Image - - - - - String - - String - - - - - - - IsAlphaPremultiplied - - - - - Switch - - Switch - - - - - NoResponse @@ -70768,20 +75180,6 @@ This can increase performance, and also silently ignore critical errors - - RandF - - - - - Float - - Float - - - - - ShaderText @@ -70797,186 +75195,224 @@ This can increase performance, and also silently ignore critical errors - ShadowBlurSize + SourceName - Int + String - Int + String - ShadowColor + Stripe - String + Float - String + Float - ShadowOffsetX + UseShaderTime - Int + Switch - Int + Switch - ShadowOffsetY + VerticalFactor - Int + Float - Int + Float - SourceName + VerticalHeight - String + Float - String + Float - UseShaderTime + VerticalShift - Switch + Float - Switch + Float - UvOffset + Vignet - System.Single[] + Float - System.Single[] + Float - - UvPixelInterval - - - - - System.Single[] + + + - System.Single[] + System.String + - - - - - UvScale - - - - - System.Single[] + + + + - System.Single[] + System.Object + - - - + + + + + + Get-OBSVersion + OBSVersion + Get + + Get-OBSVersion : GetVersion + + 0.2.0.1 + + + Gets data about the current plugin and RPC version. + Get-OBSVersion calls the OBS WebSocket with a request of type GetVersion. + + + + Get-OBSVersion + + PassThru + + If set, will return the information that would otherwise be sent to OBS. + + Switch + + Switch + + + + + + + NoResponse + + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors + + Switch + + Switch + + + + + + + + - UvSize + NoResponse - - + If set, will not attempt to receive a response from OBS. +This can increase performance, and also silently ignore critical errors - System.Single[] + Switch - System.Single[] + Switch - ViewProj + PassThru - - + If set, will return the information that would otherwise be sent to OBS. - System.Single[][] + Switch - System.Single[][] + Switch - - - - System.String - - - - - - - - - System.Object - - - - - + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + Get-OBSVersion + + + + + + + + + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getversion + + - Get-OBSVCRShader - OBSVCRShader + Get-OBSVHSShader + OBSVHSShader Get - Get-OBSVCRShader [[-VerticalShift] <float>] [[-Distort] <float>] [[-Vignet] <float>] [[-Stripe] <float>] [[-VerticalFactor] <float>] [[-VerticalHeight] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVHSShader [[-Range] <float>] [[-OffsetIntensity] <float>] [[-NoiseQuality] <float>] [[-NoiseIntensity] <float>] [[-ColorOffsetIntensity] <float>] [[-AlphaPercentage] <float>] [[-ColorToReplace] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -70984,9 +75420,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVCRShader + Get-OBSVHSShader - VerticalShift + Range @@ -71000,7 +75436,7 @@ This can increase performance, and also silently ignore critical errors - Distort + OffsetIntensity @@ -71014,7 +75450,7 @@ This can increase performance, and also silently ignore critical errors - Vignet + NoiseQuality @@ -71028,7 +75464,7 @@ This can increase performance, and also silently ignore critical errors - Stripe + NoiseIntensity @@ -71042,7 +75478,7 @@ This can increase performance, and also silently ignore critical errors - VerticalFactor + ColorOffsetIntensity @@ -71056,7 +75492,7 @@ This can increase performance, and also silently ignore critical errors - VerticalHeight + AlphaPercentage @@ -71069,8 +75505,36 @@ This can increase performance, and also silently ignore critical errors - - SourceName + + ApplyToImage + + + + + Switch + + Switch + + + + + + + ReplaceImageColor + + + + + Switch + + Switch + + + + + + + ColorToReplace @@ -71083,7 +75547,35 @@ This can increase performance, and also silently ignore critical errors + + ApplyToSpecificColor + + + + + Switch + + Switch + + + + + + SourceName + + + + + String + + String + + + + + + FilterName @@ -71097,7 +75589,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -71171,7 +75663,7 @@ This can increase performance, and also silently ignore critical errors - Distort + AlphaPercentage @@ -71185,21 +75677,21 @@ This can increase performance, and also silently ignore critical errors - FilterName + ApplyToImage - String + Switch - String + Switch - Force + ApplyToSpecificColor @@ -71213,35 +75705,35 @@ This can increase performance, and also silently ignore critical errors - NoResponse + ColorOffsetIntensity - Switch + Float - Switch + Float - PassThru + ColorToReplace - Switch + String - Switch + String - ShaderText + FilterName @@ -71255,21 +75747,21 @@ This can increase performance, and also silently ignore critical errors - SourceName + Force - String + Switch - String + Switch - Stripe + NoiseIntensity @@ -71283,7 +75775,21 @@ This can increase performance, and also silently ignore critical errors - UseShaderTime + NoiseQuality + + + + + Float + + Float + + + + + + + NoResponse @@ -71297,7 +75803,7 @@ This can increase performance, and also silently ignore critical errors - VerticalFactor + OffsetIntensity @@ -71311,21 +75817,21 @@ This can increase performance, and also silently ignore critical errors - VerticalHeight + PassThru - Float + Switch - Float + Switch - VerticalShift + Range @@ -71339,14 +75845,56 @@ This can increase performance, and also silently ignore critical errors - Vignet + ReplaceImageColor - Float + Switch - Float + Switch + + + + + + + ShaderText + + + + + String + + String + + + + + + + SourceName + + + + + String + + String + + + + + + + UseShaderTime + + + + + Switch + + Switch @@ -71374,21 +75922,22 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVersion - OBSVersion + Get-OBSVideoSettings + OBSVideoSettings Get - Get-OBSVersion : GetVersion + Get-OBSVideoSettings : GetVideoSettings 0.2.0.1 - Gets data about the current plugin and RPC version. - Get-OBSVersion calls the OBS WebSocket with a request of type GetVersion. + Gets the current video settings. + Note: To get the true FPS value, divide the FPS numerator by the FPS denominator. Example: `60000/1001` + Get-OBSVideoSettings calls the OBS WebSocket with a request of type GetVideoSettings. - Get-OBSVersion + Get-OBSVideoSettings PassThru @@ -71453,7 +76002,7 @@ This can increase performance, and also silently ignore critical errors PS > - Get-OBSVersion + Get-OBSVideoSettings @@ -71462,17 +76011,17 @@ This can increase performance, and also silently ignore critical errors - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getversion + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvideosettings - Get-OBSVHSShader - OBSVHSShader + Get-OBSVignettingShader + OBSVignettingShader Get - Get-OBSVHSShader [[-Range] <float>] [[-OffsetIntensity] <float>] [[-NoiseQuality] <float>] [[-NoiseIntensity] <float>] [[-ColorOffsetIntensity] <float>] [[-AlphaPercentage] <float>] [[-ColorToReplace] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVignettingShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-Opacity] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -71480,9 +76029,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVHSShader + Get-OBSVignettingShader - Range + InnerRadius @@ -71496,7 +76045,7 @@ This can increase performance, and also silently ignore critical errors - OffsetIntensity + OuterRadius @@ -71510,7 +76059,7 @@ This can increase performance, and also silently ignore critical errors - NoiseQuality + Opacity @@ -71524,77 +76073,7 @@ This can increase performance, and also silently ignore critical errors - NoiseIntensity - - - - - Float - - Float - - - - - - - ColorOffsetIntensity - - - - - Float - - Float - - - - - - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ReplaceImageColor - - - - - Switch - - Switch - - - - - - - ColorToReplace + Notes @@ -71607,21 +76086,7 @@ This can increase performance, and also silently ignore critical errors - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - + SourceName @@ -71635,7 +76100,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -71649,7 +76114,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -71722,76 +76187,6 @@ This can increase performance, and also silently ignore critical errors - - AlphaPercentage - - - - - Float - - Float - - - - - - - ApplyToImage - - - - - Switch - - Switch - - - - - - - ApplyToSpecificColor - - - - - Switch - - Switch - - - - - - - ColorOffsetIntensity - - - - - Float - - Float - - - - - - - ColorToReplace - - - - - String - - String - - - - - FilterName @@ -71821,21 +76216,7 @@ This can increase performance, and also silently ignore critical errors - NoiseIntensity - - - - - Float - - Float - - - - - - - NoiseQuality + InnerRadius @@ -71863,35 +76244,35 @@ This can increase performance, and also silently ignore critical errors - OffsetIntensity + Notes - Float + String - Float + String - PassThru + Opacity - Switch + Float - Switch + Float - Range + OuterRadius @@ -71905,7 +76286,7 @@ This can increase performance, and also silently ignore critical errors - ReplaceImageColor + PassThru @@ -71982,22 +76363,21 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVideoSettings - OBSVideoSettings + Get-OBSVirtualCamStatus + OBSVirtualCamStatus Get - Get-OBSVideoSettings : GetVideoSettings + Get-OBSVirtualCamStatus : GetVirtualCamStatus 0.2.0.1 - Gets the current video settings. - Note: To get the true FPS value, divide the FPS numerator by the FPS denominator. Example: `60000/1001` - Get-OBSVideoSettings calls the OBS WebSocket with a request of type GetVideoSettings. + Gets the status of the virtualcam output. + Get-OBSVirtualCamStatus calls the OBS WebSocket with a request of type GetVirtualCamStatus. - Get-OBSVideoSettings + Get-OBSVirtualCamStatus PassThru @@ -72062,7 +76442,7 @@ This can increase performance, and also silently ignore critical errors PS > - Get-OBSVideoSettings + Get-OBSVirtualCamStatus @@ -72071,17 +76451,17 @@ This can increase performance, and also silently ignore critical errors - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvideosettings + https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvirtualcamstatus - Get-OBSVignettingShader - OBSVignettingShader + Get-OBSVoronoiPixelationShader + OBSVoronoiPixelationShader Get - Get-OBSVignettingShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-Opacity] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSVoronoiPixelationShader [[-PixH] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Alternative] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -72089,37 +76469,9 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVignettingShader + Get-OBSVoronoiPixelationShader - InnerRadius - - - - - Float - - Float - - - - - - - OuterRadius - - - - - Float - - Float - - - - - - - Opacity + PixH @@ -72132,21 +76484,21 @@ This can increase performance, and also silently ignore critical errors - - Notes + + Alternative - String + Switch - String + Switch - + SourceName @@ -72160,7 +76512,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -72174,7 +76526,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -72248,21 +76600,7 @@ This can increase performance, and also silently ignore critical errors - FilterName - - - - - String - - String - - - - - - - Force + Alternative @@ -72276,21 +76614,21 @@ This can increase performance, and also silently ignore critical errors - InnerRadius + FilterName - Float + String - Float + String - NoResponse + Force @@ -72304,35 +76642,35 @@ This can increase performance, and also silently ignore critical errors - Notes + NoResponse - String + Switch - String + Switch - Opacity + PassThru - Float + Switch - Float + Switch - OuterRadius + PixH @@ -72345,20 +76683,6 @@ This can increase performance, and also silently ignore critical errors - - PassThru - - - - - Switch - - Switch - - - - - ShaderText @@ -72423,39 +76747,80 @@ This can increase performance, and also silently ignore critical errors - Get-OBSVirtualCamStatus - OBSVirtualCamStatus + Get-OBSWalkingDeadPixelFixerShader + OBSWalkingDeadPixelFixerShader Get - Get-OBSVirtualCamStatus : GetVirtualCamStatus + Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] <int>] [[-ScanOffsetX] <int>] [[-ScanOffsetY] <int>] [[-ContrastThreshold] <float>] [[-MinClusterSize] <int>] [[-MaxClusterSize] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 - Gets the status of the virtualcam output. - Get-OBSVirtualCamStatus calls the OBS WebSocket with a request of type GetVirtualCamStatus. - Get-OBSVirtualCamStatus - - PassThru + Get-OBSWalkingDeadPixelFixerShader + + ScanWidth + + + + + Int + + Int + + + + + + + ScanHeight - If set, will return the information that would otherwise be sent to OBS. + + - Switch + Int - Switch + Int - - NoResponse + + ScanOffsetX - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors + + + + Int + + Int + + + + + + + ScanOffsetY + + + + + Int + + Int + + + + + + + ShowBorder + + + Switch @@ -72465,73 +76830,8 @@ This can increase performance, and also silently ignore critical errors - - - - - NoResponse - - If set, will not attempt to receive a response from OBS. -This can increase performance, and also silently ignore critical errors - - Switch - - Switch - - - - - - - PassThru - - If set, will return the information that would otherwise be sent to OBS. - - Switch - - Switch - - - - - - - - - -------------------------- EXAMPLE 1 -------------------------- - - PS > - - Get-OBSVirtualCamStatus - - - - - - - - - https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getvirtualcamstatus - - - - - - Get-OBSVoronoiPixelationShader - OBSVoronoiPixelationShader - Get - - Get-OBSVoronoiPixelationShader [[-PixH] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Alternative] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - - 0.2.0.1 - - - - - - Get-OBSVoronoiPixelationShader - - PixH + + ContrastThreshold @@ -72544,8 +76844,36 @@ This can increase performance, and also silently ignore critical errors + + MinClusterSize + + + + + Int + + Int + + + + + + + MaxClusterSize + + + + + Int + + Int + + + + + - Alternative + ShowGreen @@ -72558,7 +76886,21 @@ This can increase performance, and also silently ignore critical errors - + + Bypass + + + + + Switch + + Switch + + + + + + SourceName @@ -72572,7 +76914,7 @@ This can increase performance, and also silently ignore critical errors - + FilterName @@ -72586,7 +76928,7 @@ This can increase performance, and also silently ignore critical errors - + ShaderText @@ -72660,7 +77002,7 @@ This can increase performance, and also silently ignore critical errors - Alternative + Bypass @@ -72673,6 +77015,20 @@ This can increase performance, and also silently ignore critical errors + + ContrastThreshold + + + + + Float + + Float + + + + + FilterName @@ -72701,6 +77057,34 @@ This can increase performance, and also silently ignore critical errors + + MaxClusterSize + + + + + Int + + Int + + + + + + + MinClusterSize + + + + + Int + + Int + + + + + NoResponse @@ -72730,14 +77114,56 @@ This can increase performance, and also silently ignore critical errors - PixH + ScanHeight - Float + Int - Float + Int + + + + + + + ScanOffsetX + + + + + Int + + Int + + + + + + + ScanOffsetY + + + + + Int + + Int + + + + + + + ScanWidth + + + + + Int + + Int @@ -72757,6 +77183,34 @@ This can increase performance, and also silently ignore critical errors + + ShowBorder + + + + + Switch + + Switch + + + + + + + ShowGreen + + + + + Switch + + Switch + + + + + SourceName @@ -72807,13 +77261,11 @@ This can increase performance, and also silently ignore critical errors - Get-OBSWalkingDeadPixelFixerShader - OBSWalkingDeadPixelFixerShader + Get-OBSZigZagShader + OBSZigZagShader Get - -Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] <int>] [[-ScanOffsetX] <int>] [[-ScanOffsetY] <int>] [[-ContrastThreshold] <float>] [[-MinClusterSize] <int>] [[-MaxClusterSize] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSZigZagShader [[-Radius] <float>] [[-Angle] <float>] [[-Period] <float>] [[-Amplitude] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-Phase] <float>] [[-Animate] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -72821,70 +77273,68 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Get-OBSWalkingDeadPixelFixerShader + Get-OBSZigZagShader - ScanWidth + Radius + + - Int + Float - Int + Float - ScanHeight + Angle + + - Int + Float - Int + Float - ScanOffsetX + Period + + - Int + Float - Int + Float - ScanOffsetY - - - Int - - Int - - - - - - - ShowBorder + Amplitude + + - Switch + Float - Switch + Float - ContrastThreshold + CenterX + + Float @@ -72895,56 +77345,66 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - MinClusterSize + CenterY + + - Int + Float - Int + Float - MaxClusterSize + Phase + + - Int + Float - Int + Float - - ShowGreen + + Animate + + - Switch + Int - Switch + Int - - Bypass + + Notes + + - Switch + String - Switch + String - + SourceName + + String @@ -72954,9 +77414,11 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - + FilterName + + String @@ -72966,9 +77428,11 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - + ShaderText + + String @@ -72981,6 +77445,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < Force + + Switch @@ -72993,6 +77459,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < PassThru + + Switch @@ -73005,6 +77473,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < NoResponse + + Switch @@ -73017,6 +77487,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < UseShaderTime + + Switch @@ -73030,20 +77502,24 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Bypass + Amplitude + + - Switch + Float - Switch + Float - ContrastThreshold + Angle + + Float @@ -73054,56 +77530,66 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - FilterName + Animate + + - String + Int - String + Int - Force + CenterX + + - Switch + Float - Switch + Float - MaxClusterSize + CenterY + + - Int + Float - Int + Float - MinClusterSize + FilterName + + - Int + String - Int + String - NoResponse + Force + + Switch @@ -73114,8 +77600,10 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - PassThru + NoResponse + + Switch @@ -73126,84 +77614,84 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - ScanHeight - - - Int - - Int - - - - - - - ScanOffsetX + Notes + + - Int + String - Int + String - ScanOffsetY + PassThru + + - Int + Switch - Int + Switch - ScanWidth + Period + + - Int + Float - Int + Float - ShaderText + Phase + + - String + Float - String + Float - ShowBorder + Radius + + - Switch + Float - Switch + Float - ShowGreen + ShaderText + + - Switch + String - Switch + String @@ -73212,6 +77700,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < SourceName + + String @@ -73224,6 +77714,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < UseShaderTime + + Switch @@ -73255,11 +77747,11 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Get-OBSZigZagShader - OBSZigZagShader + Get-OBSZoomBlurShader + OBSZoomBlurShader Get - Get-OBSZigZagShader [[-Radius] <float>] [[-Angle] <float>] [[-Period] <float>] [[-Amplitude] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-Phase] <float>] [[-Animate] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + Get-OBSZoomBlurShader [[-Samples] <int>] [[-Magnitude] <float>] [[-SpeedPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -73267,23 +77759,23 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Get-OBSZigZagShader + Get-OBSZoomBlurShader - Radius + Samples - Float + Int - Float + Int - Angle + Magnitude @@ -73297,90 +77789,48 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Period - - - - - Float - - Float - - - - - - - Amplitude - - - - - Float - - Float - - - - - - - CenterX - - - - - Float - - Float - - - - - - - CenterY + SpeedPercent - Float + Int - Float + Int - - Phase + + Ease - Float + Switch - Float + Switch - - Animate + + Glitch - Int + Switch - Int + Switch - + Notes @@ -73394,7 +77844,7 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - + SourceName @@ -73408,7 +77858,7 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - + FilterName @@ -73422,7 +77872,7 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - + ShaderText @@ -73496,63 +77946,63 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Amplitude + Ease - Float + Switch - Float + Switch - Angle + FilterName - Float + String - Float + String - Animate + Force - Int + Switch - Int + Switch - CenterX + Glitch - Float + Switch - Float + Switch - CenterY + Magnitude @@ -73565,34 +78015,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - - FilterName - - - - - String - - String - - - - - - - Force - - - - - Switch - - Switch - - - - - NoResponse @@ -73636,49 +78058,35 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Period - - - - - Float - - Float - - - - - - - Phase + Samples - Float + Int - Float + Int - Radius + ShaderText - Float + String - Float + String - ShaderText + SourceName @@ -73692,14 +78100,14 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - SourceName + SpeedPercent - String + Int - String + Int @@ -73741,11 +78149,13 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Get-OBSZoomBlurShader - OBSZoomBlurShader + Get-OBSZoomBlurTransitionShader + OBSZoomBlurTransitionShader Get - Get-OBSZoomBlurShader [[-Samples] <int>] [[-Magnitude] <float>] [[-SpeedPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + +Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] + 0.2.0.1 @@ -73753,68 +78163,46 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Get-OBSZoomBlurShader + Get-OBSZoomBlurTransitionShader - Samples + ImageA - - - Int + String - Int + String - Magnitude + ImageB - - - Float + String - Float + String - SpeedPercent - - - - - Int - - Int - - - - - - - Ease + TransitionTime - - - Switch + Float - Switch + Float - Glitch + ConvertLinear - - Switch @@ -73825,14 +78213,12 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Notes + Strength - - - String + Float - String + Float @@ -73841,8 +78227,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < SourceName - - String @@ -73855,8 +78239,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < FilterName - - String @@ -73869,8 +78251,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < ShaderText - - String @@ -73883,8 +78263,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < Force - - Switch @@ -73897,8 +78275,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < PassThru - - Switch @@ -73911,8 +78287,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < NoResponse - - Switch @@ -73925,8 +78299,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < UseShaderTime - - Switch @@ -73940,10 +78312,8 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Ease + ConvertLinear - - Switch @@ -73956,8 +78326,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < FilterName - - String @@ -73970,8 +78338,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < Force - - Switch @@ -73982,28 +78348,24 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Glitch + ImageA - - - Switch + String - Switch + String - Magnitude + ImageB - - - Float + String - Float + String @@ -74012,8 +78374,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < NoResponse - - Switch @@ -74023,25 +78383,9 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - - Notes - - - - - String - - String - - - - - PassThru - - Switch @@ -74052,24 +78396,20 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - Samples + ShaderText - - - Int + String - Int + String - ShaderText + SourceName - - String @@ -74080,28 +78420,24 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < - SourceName + Strength - - - String + Float - String + Float - SpeedPercent + TransitionTime - - - Int + Float - Int + Float @@ -74110,8 +78446,6 @@ Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] < UseShaderTime - - Switch From b2802f9eb89adbfaac4c73dba795e156c7764af7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 17 May 2026 18:56:08 +0000 Subject: [PATCH 182/266] feat: Updating /Shaders/README.md and refreshing PixelShaders --- allcommands.ps1 | 89760 ++++++++++++++++++++++++---------------------- 1 file changed, 46197 insertions(+), 43563 deletions(-) diff --git a/allcommands.ps1 b/allcommands.ps1 index 9e2a119f..b6bda071 100644 --- a/allcommands.ps1 +++ b/allcommands.ps1 @@ -1029,390 +1029,6 @@ $($ExecutionContext.SessionState.InvokeCommand.GetCommand('Send-OBS', 'Function' } } -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectName')] - [string] - $Name - ) - - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} - } - } - - process { - - if (-not $Name) { - $script:OBSFX.Values - } elseif ($script:OBSFX[$name]) { - $script:OBSFX[$name] - } - } -} -#.ExternalHelp obs-powershell-Help.xml -function Import-OBSEffect { - - - param( - # The source location of the effect. - # This can be a string, file, directory, command, or module. - [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] - [Alias( - 'FromPath', - 'FromModule', - 'FromScript', - 'FromFunction', - 'FullName', - 'Path', - 'Source' - )] - [ValidateScript({ - $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." - } - return $true - })] - - $From - ) - - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} - } - - $newEffects = @() - $obsEffectsPattern = [Regex]::new(' - (?> - ^OBS.(?>fx|effects?)\p{P} - | - [\p{P}-[-]]OBS\.(?>fx|effects?)$ - | - \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ - ) - ','IgnoreCase,IgnorePatternWhitespace') - } - - process { - # Since -From can be many things, but a metric has to be a command, - # the purpose of this function is to essentially resolve many things to a command, - # and then cache that command. - - # If -From was a string - if ($From -is [string]) { - # It could be a module, so check those first. - :ResolveFromString do { - foreach ($loadedModule in @(Get-Module)) { - # If we find the module, don't try to resolve -From as a path - if ($loadedModule.Name -eq $from) { - # (just set -From again and let the function continue) - $from = $fromModule = $loadedModule;break ResolveFromString - } - - } - # If we think from was a path - $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) - # attempt to resolve it - if ($resolvedPath) { - $from = Get-Item -LiteralPath $resolvedPath - } - } while ($false) - } - - # If -From is a module - if ($from -is [Management.Automation.PSModuleInfo]) { - # recursively call ourselves with all matching commands - @($from.ExportedCommands.Values) -match $obsEffectsPattern | - Import-OBSEffect - # then, make -From a directory - if ($from.Path) { - $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue - } - } - - # If -From is a directory - if ($from -is [IO.DirectoryInfo]) { - $FromDirectory = $from - # recursively call ourselves with all matching scripts - Get-ChildItem -LiteralPath $from.FullName -Recurse -File | - Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | - Import-OBSEffect - return - } - - # If -From is a file - if ($from -is [IO.FileInfo]) { - # and it matches the naming convention - if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } - # make -From a command. - $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') - } - - # If -From is a command - if ($from -is [Management.Automation.CommandInfo]) { - # decorate the command - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect') - } - - if ($from -is [Management.Automation.ApplicationInfo]) { - $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' - $newEffect = [PSCustomObject][Ordered]@{ - PSTypeName = 'OBS.PowerShell.Effect' - Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json - EffectName = $effectName - TypeName = $TypeName - } - $script:OBSFX[$effectName] = $newEffect - $newEffects += $newEffect - $newEffect - } else { - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') - } - # and add it to our list of new metrics - $newEffects+= $from - $script:OBSFX[$from.EffectName] = $from - $from - } - } - } - - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [Alias('Name')] - [string] - $EffectName - ) - - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} - } - } - - process { - if ($script:OBSFX[$name]) { - $script:OBSFX.Stop() - $script:OBSFX.Remove($name) - } - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSEffect -{ - - [CmdletBinding(PositionalBinding=$false)] - param( - # The name of the effect. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - $effectNames = @(Get-OBSEffect| - Select-Object -Unique -ExpandProperty EffectName) - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($effectNames -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(Mandatory)] - [string[]] - $EffectName, - - # The duration of the effect. - # If provided, all effects should use this duration. - # If not provided, each effect should use it's own duration. - [Timespan] - $Duration, - - # The parameters passed to the effect. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectParameters')] - [Collections.IDictionary] - $EffectParameter = @{}, - - # The arguments passed to the effect. - [Parameter(ValueFromRemainingArguments)] - [Alias('EffectArguments')] - [PSObject[]] - $EffectArgument = @(), - - # If provided, will step thru running - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ticks')] - [int] - $Step, - - # The SceneItemID. If this is provided, the effect will be given a target. - [Parameter(ValueFromPipelineByPropertyName)] - [int] - $SceneItemID, - - # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $SceneName, - - # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $FilterName, - - # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $SourceName, - - # If set, will loop the effect. - [switch] - $Loop, - - # If provided, will loop the effect a number of times. - [int] - $LoopCount, - - # If set, will bounce the effect (flip it / reverse it) - [switch] - $Bounce, - - # If set, will reverse an effect. - [switch] - $Reverse - ) - - process { - foreach ($NameOfEffect in $EffectName) { - $obsEffect = Get-OBSEffect -EffectName $NameOfEffect - - if (-not $obsEffect) { - Write-Warning "No Effect named '$NameOfEffect'" - continue - } - - if ($LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force - } - - if ($loop -or $Bounce) { - $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force - if (-not $LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force - } - } else { - $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force - } - - if ($Reverse) { - $obsEffect.Reversed = $true - } - - if ($obsEffect -isnot [Management.Automation.CommandInfo]) { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - $obsEffect.Start() - - } else { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - if (-not $this) { - if ($_.pstypenames -like '*.GetSourceFilter*') { - $this = $_ - } elseif ($FilterName -and $SourceName) { - $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName - } - - if ($_.pstypenames -like '*.GetSceneItem*') { - $this = $_ - } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { - $this = - foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { - if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { - $sceneItem;break - } - elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { - $sceneItem;break - } - } - } - } - - if ($Duration -and $obsEffect.Parameters.Duration) { - $EffectParameter.Duration = $Duration - } - - $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument - if ($obsEffectOutput) { - $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force - if ($step) { - $obsEffect.Step($step) - } else { - $obsEffect.Start() - } - } - } - $obsEffect - } - - - } -} -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [string] - $EffectName) - - process { - $obsEffect = Get-OBSEffect -EffectName $EffectName - - if (-not $obsEffect) { return } - - $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force - } -} #.ExternalHelp obs-powershell-Help.xml function Set-OBS3DFilter { @@ -2886,346 +2502,450 @@ function Set-OBSSharpnessFilter { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSInput { - +function Get-OBS3dPanelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] -[Alias('obs.powershell.websocket.CreateInput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, - +# Set the credits of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('credits')] +[String] +$Credits, +# Set the scale of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the tilt_x_deg of OBS3dPanelShader +[Alias('tilt_x_deg')] +[ComponentModel.DefaultBindingProperty('tilt_x_deg')] +[Single] +$TiltXDeg, +# Set the tilt_y_deg of OBS3dPanelShader +[Alias('tilt_y_deg')] +[ComponentModel.DefaultBindingProperty('tilt_y_deg')] +[Single] +$TiltYDeg, +# Set the tilt_z_deg of OBS3dPanelShader +[Alias('tilt_z_deg')] +[ComponentModel.DefaultBindingProperty('tilt_z_deg')] +[Single] +$TiltZDeg, +# Set the pos_x of OBS3dPanelShader +[Alias('pos_x')] +[ComponentModel.DefaultBindingProperty('pos_x')] +[Single] +$PosX, +# Set the pos_y of OBS3dPanelShader +[Alias('pos_y')] +[ComponentModel.DefaultBindingProperty('pos_y')] +[Single] +$PosY, +# Set the thickness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('thickness')] +[Single] +$Thickness, +# Set the radius_fb of OBS3dPanelShader +[Alias('radius_fb')] +[ComponentModel.DefaultBindingProperty('radius_fb')] +[Single] +$RadiusFb, +# Set the brightness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the light_position of OBS3dPanelShader +[Alias('light_position')] +[ComponentModel.DefaultBindingProperty('light_position')] +[Int32] +$LightPosition, +# Set the wiggle of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('wiggle')] +[Single] +$Wiggle, +# Set the wiggle_rot of OBS3dPanelShader +[Alias('wiggle_rot')] +[ComponentModel.DefaultBindingProperty('wiggle_rot')] +[Management.Automation.SwitchParameter] +$WiggleRot, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = '3d-panel' +$ShaderNoun = 'OBS3dPanelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://x.com/HoraiChan/status/1986268258883010766 +uniform string credits< + string widget_type = "info"; +> = "Based on effect by Horaiken"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float scale< + string label = "大きさ / Scale"; + string widget_type = "slider"; + float minimum = 0.25; + float maximum = 3.00; + float step = 0.001; +> = 1.0; +uniform float tilt_x_deg< + string label = "縦方向の傾き(X) / Tilt (X)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 20.0; +uniform float tilt_y_deg< + string label = "横方向の傾き(Y) / Tilt (Y)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 35.0; +uniform float tilt_z_deg< + string label = "回転(Z) / Roll (Z)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 0.0; +uniform float pos_x< + string label = "横位置 / Horizontal Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float pos_y< + string label = "縦位置 / Vertical Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float thickness< + string label = "厚み / Thickness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 0.1; + float step = 0.001; +> = 0.03; +uniform float radius_fb< + string label = "角丸 / Corner Radius"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.2; +uniform float brightness< + string label = "明るさ / Brightness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.00; + float step = 0.01; +> = 1.2; +uniform int light_position < + string label = "照明の位置 / Light Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "左側に光 / Light From Left"; + int option_1_value = 1; + string option_1_label = "右側に光 / Light From Right"; +> = 0; +uniform float wiggle < + string label = "ゆらゆら / Wiggle"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.50; + float step = 0.01; +> = 0.0; +uniform bool wiggle_rot < + string label = "角度もゆらゆら / Wiggle Rotation"; +>; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float hash1(float n){ return frac(sin(n)*43758.5453123); } +float noise1D(float x) { + float i = floor(x); + float f = frac(x); + float u = f*f*(3.0 - 2.0*f); + return lerp(hash1(i), hash1(i+1.0), u); // 0..1 } +float fbm1D(float x) { + float v = 0.0; + float a = 0.5; + float f = 1.0; + for(int k=0;k<4;k++){ + v += a * noise1D(x * f); + f *= 2.0; + a *= 0.5; + } + return v; +} -} +float saturate(float x) { return clamp(x, 0.0, 1.0); } - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSProfile { +float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } +float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } +float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } +// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) +float sdRoundRect2D(float2 p, float2 bxy, float r) { + float2 q = abs(p) - bxy + r; + return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] -[Alias('obs.powershell.websocket.CreateProfile')] -param( +// 正面シルエット角丸 + Z方向に押し出し +float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { + float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); + float a = sdRoundRect2D(p.xy, b.xy, r_fb); + float dz = abs(p.z) - b.z; + return max(a, dz); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// 法線 +float3 calcNormal(float3 p, float3 b, float rfb) { + const float e = 0.001; + float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); + float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); + float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); + float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); + return normalize(float3(dx,dy,dz)); +} +// 照明 +float3 shade(float3 n, float3 v) { + float3 l; + if (light_position == 0) { // 左から光 + l = normalize(float3(-1.0, -0.1, 1.0)); + } + else { // 右から光 + l = normalize(float3( 1.0, -0.1, 1.0)); + } + float diff = saturate(dot(n,l)); + float rim = pow(1.0 - saturate(dot(n,v)), 2.0); + float li = 0.25 + 0.75*diff + 0.08*rim; + return float3(li, li, li); +} -process { +float4 mainImage(VertData v_in) : TARGET { + float2 uv = v_in.uv; + // 画面座標(短辺基準) + float aspect = uv_size.x / uv_size.y; + float2 ndc = uv * 2.0 - 1.0; + ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); + float2 p2 = ndc; + p2.x *= aspect; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + // カメラ設定 + float3 ro = float3(0.0, 0.0, 3.2); + float3 rd = normalize(float3(p2, -4.0)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + // 回転(Z→Y→X の順に逆回転) + float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); + ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); + rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + // 画面フィット(短辺基準)+ 厚み + float2 baseXY; + if (aspect > 1.0) { + baseXY = float2(1.0, 1.0 / aspect); + } else { + const float portraitMargin = 0.6; + baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); + } + float3 b = float3(baseXY, thickness) * max(scale, 0.0001); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + // Wiggle + float diag = length(2.0 * b); + float amp = 0.05 * wiggle * diag; + const float WSPD = 0.1; - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + // 各軸に独立ノイズ + float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; + float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; + float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; + float3 woff = float3(wx, wy, wz); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + float rotAmp = radians(12.0) * wiggle; + + float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; + float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; + + float3 ro2 = ro; + float3 rd2 = rd; + + if (wiggle_rot) { + ro2 = rotateX(ro2, wobX); + ro2 = rotateY(ro2, wobY); + rd2 = rotateX(rd2, wobX); + rd2 = rotateY(rd2, wobY); + } -} + float t = 0.0; + float d = 0.0; + bool hit = false; + for (int i=0; i<64; i++) { + float3 pos = ro2 + rd2 * t; + d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); + if (d < 0.001) { hit = true; break; } + t += d; + if (t > 8.0) break; + } + // ヒットしなければ完全透明(元ソースは非表示) + if (!hit) return float4(0.0, 0.0, 0.0, 0.0); -} + float3 pos = ro2 + rd2 * t; + float3 pObj = pos - woff; + float3 n = calcNormal(pObj, b, radius_fb); + float3 vdir = normalize(-rd2); - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSScene { + // テクスチャ貼り付け + float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); + float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; + // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) + float4 texFront = image.Sample(textureSampler, uvTex); + float4 texEdge = image.Sample(textureSampler, uvTex); + float4 tex = lerp(texEdge, texFront, frontMask); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] -[Alias('obs.powershell.websocket.CreateScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // フロント面エッジ・ハイライト(細い線) + float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); + float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF + float edgeWidth = 0.02 * min(b.x, b.y); + float edgeIntensity = 0.6; + float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); + float edgeMask = frontMask * edgeProx; + tex.rgb *= (1.0 + edgeMask * edgeIntensity); + // 照明 + float3 lightTerm = shade(n, vdir); + tex.rgb *= lightTerm; -process { + // 明るさスライダ + tex.rgb *= brightness; + // 出力 + return float4(tex.rgb, 1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -3234,105 +2954,269 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneCollection { - +function Get-OBS3dSwapTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] -[Alias('obs.powershell.websocket.CreateSceneCollection')] +[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the image_a of OBS3dSwapTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBS3dSwapTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBS3dSwapTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBS3dSwapTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the reflection of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('reflection')] +[Single] +$Reflection, +# Set the perspective of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the depth of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('depth')] +[Single] +$Depth, +# Set the background_color of OBS3dSwapTransitionShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] +[String] +$BackgroundColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = '3d_swap_transition' +$ShaderNoun = 'OBS3dSwapTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/MlXGzf +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float reflection< + string label = "Reflection (0.4)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.4; +uniform float perspective< + string label = "Perspective (0.2)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = .2; +uniform float depth< + string label = "Depth (3.0)"; + string widget_type = "slider"; + float minimum = 1.00; + float maximum = 10.00; + float step = 0.1; +> = 3.; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +#ifndef OPENGL +#define lessThan(a,b) (a < b) +#endif - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + +uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; + +bool inBounds (float2 p) { + return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); +} + +float2 project (float2 p) { + return p * float2(1.0, -1.2) + float2(0.0, 2.22); +} + +float4 bgColor (float2 p, float2 pfr, float2 pto) { + float4 c = background_color; + pfr = project(pfr); + if (inBounds(pfr)) { + c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); + } + pto = project(pto); + if (inBounds(pto)) { + c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); + } + return c; +} + +float4 mainImage(VertData v_in) : TARGET { + float2 p = v_in.uv; + float2 pfr = float2(-1.,-1.); + float2 pto = float2(-1.,-1.); + + float progress = transition_time; + float size = lerp(1.0, depth, progress); + float persp = perspective * progress; + pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); + + size = lerp(1.0, depth, 1.-progress); + persp = perspective * (1.-progress); + pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); + + bool fromOver = progress < 0.5; + float4 rgba = background_color; + if (fromOver) { + if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + else { + if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -3341,127 +3225,147 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneItem { - +function Get-OBSAddShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] -[Alias('obs.powershell.websocket.CreateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -[Alias('Add-OBSSceneSource')] +[Alias('Set-OBSAddShader','Add-OBSAddShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the other_image of OBSAddShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] +[String] +$OtherImage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Add' +$ShaderNoun = 'OBSAddShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d other_image; +float4 mainImage(VertData v_in) : TARGET +{ + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return clamp(base + other, 0.0, 1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -3470,125 +3374,182 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSourceFilter { - +function Get-OBSAlphaBorderShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] -[Alias('obs.powershell.websocket.CreateSourceFilter')] +[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] param( - +# Set the border_color of OBSAlphaBorderShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_thickness of OBSAlphaBorderShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the alpha_cut_off of OBSAlphaBorderShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'alpha_border' +$ShaderNoun = 'OBSAlphaBorderShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4 border_color< + string label = "Border color"; +> = {0.0,0.0,0.0,1.0}; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float4 pix = image.Sample(textureSampler, v_in.uv); + if (pix.a > alpha_cut_off) + return pix; + [loop] for(int x = -border_thickness;x alpha_cut_off) + return border_color; + } + } + } + return pix; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -3597,230 +3558,300 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Copy-OBSSceneItem { - +function Get-OBSAlphaGamingBentCameraShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] -[Alias('obs.powershell.websocket.DuplicateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] param( - +# Set the left_side_width of OBSAlphaGamingBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] +[Single] +$LeftSideWidth, +# Set the left_side_size of OBSAlphaGamingBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] +[Single] +$LeftSideSize, +# Set the left_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] +[Single] +$LeftSideShadow, +# Set the left_flip_width of OBSAlphaGamingBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] +[Single] +$LeftFlipWidth, +# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] +[Single] +$LeftFlipShadow, +# Set the right_side_width of OBSAlphaGamingBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] +[Single] +$RightSideWidth, +# Set the right_side_size of OBSAlphaGamingBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] +[Single] +$RightSideSize, +# Set the right_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] +[Single] +$RightSideShadow, +# Set the right_flip_width of OBSAlphaGamingBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] +[Single] +$RightFlipWidth, +# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneName')] -[string] -$DestinationSceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] -[string] -$DestinationSceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'alpha-gaming-bent-camera' +$ShaderNoun = 'OBSAlphaGamingBentCameraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float left_side_width< + string label = "Left side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float left_flip_shadow< + string label = "Left flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; +uniform float right_side_width< + string label = "Right side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; + } + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentPreviewScene { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -3829,204 +3860,270 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentProgramScene { - +function Get-OBSAnimatedPathShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] -[Alias('obs.powershell.websocket.GetCurrentProgramScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAnimatedPathShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSAnimatedPathShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedPathShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedPathShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedPathShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the speed_percent of OBSAnimatedPathShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the path_map of OBSAnimatedPathShader +[Alias('path_map')] +[ComponentModel.DefaultBindingProperty('path_map')] +[String] +$PathMap, +# Set the reverse of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'animated_path' +$ShaderNoun = 'OBSAnimatedPathShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int speed_percent = 100; +uniform texture2d path_map; +uniform bool reverse = false; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + float3 pos = v_in.pos.xyz; + float3 current_pos; + float speed = speed_percent * 0.01; + //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float t = 1.0 + sin(elapsed_time * speed) ; + // combine luma texture and user defined shine color + float luma = path_map.Sample(textureSampler, v_in.uv).x; + if (reverse) + { + luma = 1.0 - luma; + } -} + float time = lerp(0.0f, 1.0f , t - 1.0); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransition { + // set current position in time + current_pos.x = 0; + current_pos.y = 0; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float2 offset = uv_offset; + if (speed == 0.0f) + { + offset.x = 0.0f; + offset.y = 0.0f; + } + else + { + offset.x = uv_offset.x + time * luma; + offset.y = uv_offset.y + time * luma; + } + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} -process { +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4035,317 +4132,403 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransitionCursor { - +function Get-OBSAnimatedTextureShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAnimatedTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSAnimatedTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSAnimatedTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the Animation_Image of OBSAnimatedTextureShader +[Alias('Animation_Image')] +[ComponentModel.DefaultBindingProperty('Animation_Image')] +[String] +$AnimationImage, +# Set the Colorization_Image of OBSAnimatedTextureShader +[Alias('Colorization_Image')] +[ComponentModel.DefaultBindingProperty('Colorization_Image')] +[String] +$ColorizationImage, +# Set the reverse of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# Set the bounce of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('bounce')] +[Management.Automation.SwitchParameter] +$Bounce, +# Set the center_animation of OBSAnimatedTextureShader +[Alias('center_animation')] +[ComponentModel.DefaultBindingProperty('center_animation')] +[Management.Automation.SwitchParameter] +$CenterAnimation, +# Set the polar_animation of OBSAnimatedTextureShader +[Alias('polar_animation')] +[ComponentModel.DefaultBindingProperty('polar_animation')] +[Management.Automation.SwitchParameter] +$PolarAnimation, +# Set the polar_angle of OBSAnimatedTextureShader +[Alias('polar_angle')] +[ComponentModel.DefaultBindingProperty('polar_angle')] +[Single] +$PolarAngle, +# Set the polar_height of OBSAnimatedTextureShader +[Alias('polar_height')] +[ComponentModel.DefaultBindingProperty('polar_height')] +[Single] +$PolarHeight, +# Set the speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] +[Single] +$SpeedHorizontalPercent, +# Set the speed_vertical_percent of OBSAnimatedTextureShader +[Alias('speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] +[Single] +$SpeedVerticalPercent, +# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('tint_speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] +[Single] +$TintSpeedHorizontalPercent, +# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader +[Alias('tint_speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] +[Single] +$TintSpeedVerticalPercent, +# Set the Alpha of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('Alpha')] +[Single] +$Alpha, +# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader +[Alias('Use_Animation_Image_Color')] +[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] +[Management.Automation.SwitchParameter] +$UseAnimationImageColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'animated_texture' +$ShaderNoun = 'OBSAnimatedTextureShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 +// Animates a texture with polar sizing and color options +// for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform texture2d Animation_Image; +uniform texture2d Colorization_Image; +uniform bool reverse = false; +uniform bool bounce = false; +uniform bool center_animation = true; +uniform bool polar_animation = true; +uniform float polar_angle = 90.0; +uniform float polar_height = 1.0; +uniform float speed_horizontal_percent = 50; +uniform float speed_vertical_percent = 5; +uniform float tint_speed_horizontal_percent = 50; +uniform float tint_speed_vertical_percent = 5; +uniform float Alpha = 1.0; +uniform bool Use_Animation_Image_Color = true; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +float4 convert_pmalpha(float4 color) +{ + float4 ret = color; + if (color.a >= 0.001) + ret.xyz /= color.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; } +float2 time(float2 speed_dir) +{ + float PI = 3.1415926535897932384626433832795; //acos(-1); -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroup { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] -[Alias('obs.powershell.websocket.GetGroupList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - + float2 t = (elapsed_time * speed_dir) ; + if (bounce) + { + // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen + t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; + t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; + t *= -0.5; + } -process { + if (reverse) + t = t * -1; + return t; +} +VertData mainTransform(VertData v_in) +{ + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + VertData vert_out; + //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float2 offset = uv_offset; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + if (center_animation) + { + vert_out.uv = v_in.uv - 0.5f; + } + else + { + offset += time(speed_dir); + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + return vert_out; +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } -} +float4 mainImage(VertData v_in) : TARGET +{ + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); -} + //compensate for background vertex shader values + float2 background_offset = float2(-.5,-.5); + if (!center_animation) + background_offset = time(speed_dir); + float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroupSceneItem { + // Convert our texture coordinates to polar form: + if (polar_animation) { + + float2 polar = float2( + atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle + log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius + ); + // Check how much our texture sampling point changes between + // neighbouring pixels to the sides (ddx) and above/below (ddy) + ///float4 gradient = float4(ddx(polar), ddy(polar)); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] -[Alias('obs.powershell.websocket.GetGroupSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + // If our angle wraps around between adjacent samples, + // discard one full rotation from its value and keep the fraction. + ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + float2 tintUVs = polar * 4; + tintUVs += time(tint_speed_dir); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Apply texture scale + polar *= 4; + // Scroll the texture over time. + polar += time(speed_dir); + float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); + + float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); + float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); + //animation.a = clamp((intensity),0.0,1.0); + if (Use_Animation_Image_Color) + { + animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + else + { + animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + //if (keyAmount > 0.5f) + rgba = lerp(rgba, animation, animation.a * Alpha); + } -process { + return rgba; +} +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4354,101 +4537,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHotkey { - +function Get-OBSAsciiShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] -[Alias('obs.powershell.websocket.GetHotkeyList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the scale of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('scale')] +[Int32] +$Scale, +# Set the base_color of OBSAsciiShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] +[String] +$BaseColor, +# Set the monochrome of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('monochrome')] +[Management.Automation.SwitchParameter] +$Monochrome, +# Set the character_set of OBSAsciiShader +[Alias('character_set')] +[ComponentModel.DefaultBindingProperty('character_set')] +[Int32] +$CharacterSet, +# Set the note of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('note')] +[String] +$Note, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'ascii' +$ShaderNoun = 'OBSAsciiShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 +// https://github.com/Oncorporation/obs-shaderfilter +// Based on the following shaders: +// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 +// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 +// Modifications of original shaders include: +// - Porting from GLSL to HLSL +// - Combining characters sets from both source shaders +// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set +// +// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ +// converts a bitmap into int then decodes it to look like text - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int scale< + string label = "Scale"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; // Size of characters +uniform float4 base_color< + string label = "Base color"; +> = {0.0,1.0,0.0,1.0}; // Monochrome base color +uniform bool monochrome< + string label = "Monochrome"; +> = false; +uniform int character_set< + string label = "Character set"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Large set of non-letters"; + int option_1_value = 1; + string option_1_label = "Small set of capital letters"; +> = 0; +uniform string note< + string widget_type = "info"; +> = "Base color is used as monochrome base color."; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float character(int n, float2 p) +{ + p = floor(p*float2(4.0, 4.0) + 2.5); + if (clamp(p.x, 0.0, 4.0) == p.x) + { + if (clamp(p.y, 0.0, 4.0) == p.y) + { + int a = int(round(p.x) + 5.0 * round(p.y)); + if (((n >> a) & 1) == 1) return 1.0; + } + } + return 0.0; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x/y); +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 iResolution = uv_size; + float2 pix = v_in.uv * iResolution; + float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); + + float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + + int n; + int charset = clamp(character_set, 0, 1); + + if (charset==0) + { + if (gray <= 0.2) n = 4096; // . + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 332772; // * + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 23385164; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13199452; // @ + if (gray > 0.8) n = 11512810; // # + } + else if (charset==1) + { + if (gray <= 0.1) n = 0; + if (gray > 0.1) n = 9616687; // R + if (gray > 0.3) n = 32012382; // S + if (gray > 0.5) n = 16303663; // D + if (gray > 0.7) n = 15255086; // O + if (gray > 0.8) n = 16301615; // B + } + + float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); + + if (monochrome) + { + c.rgb = base_color.rgb; + } + c = c*character(n, p); + + return c; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4457,106 +4795,272 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInput { - +function Get-OBSAspectRatioShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] -[Alias('obs.powershell.websocket.GetInputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] param( - +# Set the ViewProj of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAspectRatioShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSAspectRatioShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAspectRatioShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAspectRatioShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAspectRatioShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSAspectRatioShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the borderColor of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('borderColor')] +[String] +$BorderColor, +# Set the notes of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'aspect_ratio' +$ShaderNoun = 'OBSAspectRatioShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// variables +uniform float4 borderColor = {0,0,0,0}; +float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; +uniform string notes; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + + float2 hw = uv_scale; + // determine the game window''s current aspect ratio + float windowaspect = hw.x / hw.y; + + // current viewport height should be scaled by this amount + float scaleheight = windowaspect / targetaspect; + + + // if scaled height is less than current height, add letterbox + if (scaleheight < 1.0f) + { + Rect rect = camera.rect; + + rect.width = 1.0f; + rect.height = scaleheight; + rect.x = 0; + rect.y = (1.0f - scaleheight) / 2.0f; + + camera.rect = rect; + } + else // add pillarbox + { + float scalewidth = 1.0f / scaleheight; + + Rect rect = camera.rect; + + rect.width = scalewidth; + rect.height = 1.0f; + rect.x = (1.0f - scalewidth) / 2.0f; + rect.y = 0; + + camera.rect = rect; + } + return vert_out; +} + +float4 mainImage(VertData v_in) : TARGET +{ + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4565,111 +5069,190 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioBalance { - +function Get-OBSAudioShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] -[Alias('obs.powershell.websocket.GetInputAudioBalance')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSAudioShader','Add-OBSAudioShader')] param( - +# Set the audio_peak of OBSAudioShader +[Alias('audio_peak')] +[ComponentModel.DefaultBindingProperty('audio_peak')] +[Single] +$AudioPeak, +# Set the audio_magnitude of OBSAudioShader +[Alias('audio_magnitude')] +[ComponentModel.DefaultBindingProperty('audio_magnitude')] +[Single] +$AudioMagnitude, +# Set the intensity of OBSAudioShader +[ComponentModel.DefaultBindingProperty('intensity')] +[Single] +$Intensity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'audio' +$ShaderNoun = 'OBSAudioShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Audio shader example showing the difference between audio_peak and audio_magnitude. +// Left half uses audio_peak (red), right half uses audio_magnitude (blue). +uniform float audio_peak; +uniform float audio_magnitude; + +uniform float intensity < + string label = "Audio intensity"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 3.0; + float step = 0.1; +> = 1.0; +float4 mainImage(VertData v_in) : TARGET { + float4 color = image.Sample(textureSampler, v_in.uv); + + // Split screen based on UV coordinate + if (v_in.uv.x < 0.5) { + // Left half: audio_peak (instantaneous spikes, more reactive) + // Tint with red to show peak activity. + float peak_strength = audio_peak * intensity; + float3 peak_color = color.rgb + float3(peak_strength, 0, 0); + return float4(peak_color, color.a); + } else { + // Right half: audio_magnitude (RMS/averaged levels, smoother) + // Tint with blue to show magnitude activity. + float mag_strength = audio_magnitude * intensity; + float3 mag_color = color.rgb + float3(0, 0, mag_strength); + return float4(mag_color, color.a); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +/* +EXPLANATION: +- audio_peak: Shows instantaneous maximum levels, very responsive to drums/percussion. +- audio_magnitude: Shows RMS (Root Mean Square) levels, smoother and represents sustained audio. + +TYPICAL BEHAVIOR: +- With music containing drums: Left side (peak) will flash more dramatically on beats. +- With sustained tones: Right side (magnitude) will show more consistent levels. +- Peak reacts faster to sudden sounds, magnitude is more stable for smooth effects. +*/ - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4678,224 +5261,315 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioMonitorType { - +function Get-OBSBackgroundRemovalShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the ViewProj of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSBackgroundRemovalShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSBackgroundRemovalShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSBackgroundRemovalShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSBackgroundRemovalShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSBackgroundRemovalShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSBackgroundRemovalShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the target of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('target')] +[String] +$Target, +# Set the color of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the opacity of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# Set the invert of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the Convert_709to601 of OBSBackgroundRemovalShader +[Alias('Convert_709to601')] +[ComponentModel.DefaultBindingProperty('Convert_709to601')] +[Management.Automation.SwitchParameter] +$Convert709to601, +# Set the Convert_601to709 of OBSBackgroundRemovalShader +[Alias('Convert_601to709')] +[ComponentModel.DefaultBindingProperty('Convert_601to709')] +[Management.Automation.SwitchParameter] +$Convert601to709, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'background_removal' +$ShaderNoun = 'OBSBackgroundRemovalShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Exeldro February 19, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform texture2d target; +uniform float4 color; +uniform float opacity = 15.0; +uniform bool invert; +uniform bool Convert_709to601; +uniform bool Convert_601to709; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; +float dot(float3 a,float3 b){ + return a.x*b.x+a.y*b.y+a.z*b.z; } +//BT.601 to BT.709 +// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input +// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. +float4 Convert601to709(float4 rgba) +{ + float3 s1 = rgba.rgb; + s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace + return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioSyncOffset { - +//BT.709 to BT.601 +float4 Convert709to601(float4 rgba) +{ + float3 s1 = rgba.rgb; + s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); + return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +VertDataOut VSDefault(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); + vert_out.uv = v_in.uv; + vert_out.uv2 = v_in.uv * uv_scale + uv_offset; + return vert_out; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET +{ + float Tolerance = opacity * 0.01; + float4 rgba = image.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; + if (invert){ + targetRGB.rgb = 1.0 - targetRGB.rgb; + } + if (Convert_709to601) + { + rgba.rgb = Convert709to601(rgba).rgb; + targetRGB.rgb = Convert709to601(targetRGB).rgb; + } + if (Convert_601to709) + { + rgba.rgb = Convert601to709(rgba).rgb; + targetRGB.rbg = Convert601to709(targetRGB).rgb; + } -process { + float4 shadowRGB = targetRGB * targetRGB; + if ((abs(targetRGB.r - rgba.r) <= Tolerance && + abs(targetRGB.g - rgba.g) <= Tolerance && + abs(targetRGB.b - rgba.b) <= Tolerance) + || (abs(shadowRGB.r - rgba.r) <= Tolerance && + abs(shadowRGB.g - rgba.g) <= Tolerance && + abs(shadowRGB.b - rgba.b) <= Tolerance)) + { + rgba.rgba = float4(0,0,0,0); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSColorMaskRGBA(v_in); + } +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -4904,111 +5578,255 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioTracks { - +function Get-OBSBlendOpacityShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] -[Alias('obs.powershell.websocket.GetInputAudioTracks')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Vertical of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the Rotational of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Rotational')] +[Management.Automation.SwitchParameter] +$Rotational, +# Set the Rotation_Offset of OBSBlendOpacityShader +[Alias('Rotation_Offset')] +[ComponentModel.DefaultBindingProperty('Rotation_Offset')] +[Single] +$RotationOffset, +# Set the Opacity_Start_Percent of OBSBlendOpacityShader +[Alias('Opacity_Start_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] +[Single] +$OpacityStartPercent, +# Set the Opacity_End_Percent of OBSBlendOpacityShader +[Alias('Opacity_End_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] +[Single] +$OpacityEndPercent, +# Set the Spread of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Spread')] +[Single] +$Spread, +# Set the Speed of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Notes of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'blend_opacity' +$ShaderNoun = 'OBSBlendOpacityShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 14, 2022 +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 6.28318531; + float step = 0.01; +> = 0.0; +uniform float Opacity_Start_Percent< + string label = "Opacity Start Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 0.0; +uniform float Opacity_End_Percent< + string label = "Opacity End Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform float Spread< + string label = "Spread"; + string widget_type = "slider"; + float minimum = 0.25; + float maximum = 10.0; + float step = 0.01; +> = 0.5; +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.0; +uniform bool Apply_To_Alpha_Layer = true; +uniform string Notes< + string widget_type = "info"; +> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 point_color = image.Sample(textureSampler, v_in.uv); + float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; + float4 gray = float4(luminance,luminance,luminance, 1); + float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); + float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) + { + //set opacity and direction + float opacity = (-1 * lPos.x) * 0.5; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + if (Vertical && (Rotational == false)) + { + opacity = (-1 * lPos.y) * 0.5; + } + + opacity += time; + opacity = frac(opacity); + point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); + } + return point_color; +} + + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5017,106 +5835,152 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputDefaultSettings { - +function Get-OBSBlinkShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] -[Alias('obs.powershell.websocket.GetInputDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed of OBSBlinkShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'blink' +$ShaderNoun = 'OBSBlinkShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float t = elapsed_time * speed; + return float4(color.r, color.g, color.b, color.a * (1 + sin(t)) / 2); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5125,219 +5989,218 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputKind { - +function Get-OBSBloomShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] -[Alias('obs.powershell.websocket.GetInputKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBloomShader','Add-OBSBloomShader')] param( - +# Set the Angle_Steps of OBSBloomShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] +[Int32] +$AngleSteps, +# Set the Radius_Steps of OBSBloomShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSBloomShader +[ComponentModel.DefaultBindingProperty('ampFactor')] +[Single] +$AmpFactor, +# Set the notes of OBSBloomShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('unversioned')] -[switch] -$Unversioned, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'bloom' +$ShaderNoun = 'OBSBloomShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Bloom shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 15, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 5; // +uniform int Radius_Steps< + string label = "Radius Steps"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 9; // +uniform float ampFactor< + string label = "amp Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 2.0; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; +float4 mainImage(VertData v_in) : TARGET +{ + int radiusSteps = clamp(Radius_Steps, 0, 20); + int angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + float minRadius = (0.0 * uv_pixel_interval.y); + float maxRadius = (10.0 * uv_pixel_interval.y); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputMute { + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 outputPixel = c0; + float4 accumulatedColor = float4(0,0,0,0); + int totalSteps = radiusSteps * angleSteps; + float angleDelta = (2.0 * PI) / float(angleSteps); + float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] -[Alias('obs.powershell.websocket.GetInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + float(radiusStep) * radiusDelta; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { + float2 currentCoord; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor =image.Sample(textureSampler, currentCoord); + float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); + accumulatedColor += currentFraction * currentColor / float(totalSteps); + + } + } -process { + outputPixel += accumulatedColor * ampFactor; + return outputPixel; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5346,116 +6209,151 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputPropertiesListPropertyItems { - +function Get-OBSBorderShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] -[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] param( - +# Set the borderColor of OBSBorderShader +[ComponentModel.DefaultBindingProperty('borderColor')] +[String] +$BorderColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'border' +$ShaderNoun = 'OBSBorderShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4 borderColor; +float4 mainImage(VertData v_in) : TARGET +{ + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5464,111 +6362,237 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputSettings { - +function Get-OBSBoxBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] -[Alias('obs.powershell.websocket.GetInputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Strength of OBSBoxBlurShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Int32] +$Strength, +# Set the Mask_Left of OBSBoxBlurShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSBoxBlurShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSBoxBlurShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSBoxBlurShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'box-blur' +$ShaderNoun = 'OBSBoxBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int Strength< + string label = "Strength (1)"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 1; +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +float4 mainImage(VertData v_in) : TARGET +{ + if(Strength <= 0) + return image.Sample(textureSampler, v_in.uv); + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + float transparent = 0.0; + int count = 1; + float samples = 0.0; + float4 c = float4(0.0, 0.0, 0.0, 0.0); + float Steps = float(Strength); + + [loop] for (int i = -Strength; i <= Strength; i++) { + [loop] for (int k = -Strength; k <= Strength; k++) { + float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); + transparent += sc.a; + count++; + c += sc * sc.a; + samples += sc.a; + } + } + if(samples > 0.0) + c /= samples; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + c.a = transparent / float(count); + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5577,214 +6601,227 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputVolume { - +function Get-OBSBulgePinchShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] -[Alias('obs.powershell.websocket.GetInputVolume')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} +# Set the radius of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the magnitude of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('magnitude')] +[Single] +$Magnitude, +# Set the center_x of OBSBulgePinchShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSBulgePinchShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the animate of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the notes of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) -} +process { +$shaderName = 'BulgePinch' +$ShaderNoun = 'OBSBulgePinchShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 0.0; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = -1.3333; + float maximum = 1.3333; + float step = 0.01; +> = 0.0; +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform bool animate = false; - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSLastReplayBufferReplay { +uniform string notes< + string widget_type = "info"; +> = "Distorts the screen, expanding or drawing in pixels around a point." -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] -[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET +{ + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + v_out.uv = 1. * v_in.uv - center; + center.x /= ar; + v_out.uv.x /= ar; -process { + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); + float percent = dist/radius; + if(anim_mag > 0) + v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); + else + v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); + v_out.uv += (2 * center); + v_out.uv.x *= ar; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -5793,214 +6830,343 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMediaInputStatus { - +function Get-OBSBurnShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] -[Alias('obs.powershell.websocket.GetMediaInputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Burn_Gradient of OBSBurnShader +[Alias('Burn_Gradient')] +[ComponentModel.DefaultBindingProperty('Burn_Gradient')] +[String] +$BurnGradient, +# Set the Speed of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Gradient_Adjust of OBSBurnShader +[Alias('Gradient_Adjust')] +[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] +[Single] +$GradientAdjust, +# Set the Dissolve_Value of OBSBurnShader +[Alias('Dissolve_Value')] +[ComponentModel.DefaultBindingProperty('Dissolve_Value')] +[Single] +$DissolveValue, +# Set the Animated of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Animated')] +[Management.Automation.SwitchParameter] +$Animated, +# Set the Apply_to_Channel of OBSBurnShader +[Alias('Apply_to_Channel')] +[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] +[Management.Automation.SwitchParameter] +$ApplyToChannel, +# Set the Apply_Smoke of OBSBurnShader +[Alias('Apply_Smoke')] +[ComponentModel.DefaultBindingProperty('Apply_Smoke')] +[Management.Automation.SwitchParameter] +$ApplySmoke, +# Set the Smoke_Horizonal_Speed of OBSBurnShader +[Alias('Smoke_Horizonal_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] +[Single] +$SmokeHorizonalSpeed, +# Set the Smoke_Vertical_Speed of OBSBurnShader +[Alias('Smoke_Vertical_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] +[Single] +$SmokeVerticalSpeed, +# Set the Iterations of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Iterations')] +[Int32] +$Iterations, +# Set the Notes of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'burn' +$ShaderNoun = 'OBSBurnShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Exeldro February 17, 2022 +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} +float4 mod289(float4 x) +{ + return x - floor(x / 289.0) * 289.0; +} +float4 permute(float4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} +float4 taylorInvSqrt(float4 r) +{ + return 1.79284291400159 - r * 0.85373472095314; +} +float2 fade(float2 t) { + return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); +} +float dot(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +// Classic Perlin noise +float cnoise(float2 P) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} +// Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; } +uniform texture2d Burn_Gradient = "burngradient.png"; +uniform float Speed = 0.33; +uniform float Gradient_Adjust = 0.85; +uniform float Dissolve_Value = 1.43; +uniform bool Animated; +uniform bool Apply_to_Channel; +uniform bool Apply_Smoke = true; +uniform float Smoke_Horizonal_Speed = 0.3; +uniform float Smoke_Vertical_Speed = 0.17; +uniform int Iterations = 4; +uniform string Notes< + string widget_type = "info"; +> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; -} +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float4 smoke = float4(1.0,1.0,1.0,1.0); + float4 result = smoke; + float t = elapsed_time * Speed; + float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay + float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); + //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSMonitor { + float perlin = 0.5; + float smoke_perlin = 0; + float scale = 1; + float w = 0.5; + for (int i = 0; i < Iterations; i++) { + //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; + float2 coord = (v_in.uv + t * (dir * .1)) * scale; + float2 period = scale * dir; + perlin += pnoise(coord, period) * w; + if (Apply_Smoke) + smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] -[Alias('obs.powershell.websocket.GetMonitorList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + scale *= 2.0; + w *= 0.5; + } + //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); + if (!Animated) + cycle = 1; + float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); + float overOne = saturate(d * Gradient_Adjust); + float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); -process { + if (Apply_to_Channel) { + result = c * burn; + } + else { + result = float4(perlin, perlin, perlin, 1.0) * burn; + } + if (smoke_perlin > 0) { + smoke *= smoke_perlin; + if (result.a <= 0.04) + result = float4(smoke.rgb, smoke_perlin); + result += smoke; + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return result; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6009,209 +7175,286 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutput { - +function Get-OBSCartoonShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] -[Alias('obs.powershell.websocket.GetOutputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSCartoonShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSCartoonShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSCartoonShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSCartoonShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSCartoonShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSCartoonShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the hue_steps of OBSCartoonShader +[Alias('hue_steps')] +[ComponentModel.DefaultBindingProperty('hue_steps')] +[Int32] +$HueSteps, +# Set the value_steps of OBSCartoonShader +[Alias('value_steps')] +[ComponentModel.DefaultBindingProperty('value_steps')] +[Int32] +$ValueSteps, +# Set the Apply_To_Alpha_Layer of OBSCartoonShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'cartoon' +$ShaderNoun = 'OBSCartoonShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes = "5/2 seems reasonable"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int hue_steps = 5; +uniform int value_steps = 2; +uniform bool Apply_To_Alpha_Layer = true; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertDataOut VSDefault(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; } +float3 rgb2hsv(float3 c) +{ + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputSettings { - + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] -[Alias('obs.powershell.websocket.GetOutputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float fit(float v, int factor) +{ + return round(v * factor) / factor; +} +float hue_wrap(float h) +{ + return fmod(h + 1, 2) - 1; + if(h > 1) + return h - 2; + if(h < -1) + return h + 2; + return h; +} -process { +float4 PassThrough(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float3 hsv = rgb2hsv(rgba.rgb); + hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); + //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); + rgba = float4(hsv2rgb(hsv), rgba.a); + //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); + } + return rgba; +} +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PassThrough(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6220,106 +7463,217 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputStatus { - +function Get-OBSCellShadedShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] -[Alias('obs.powershell.websocket.GetOutputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Angle_Steps of OBSCellShadedShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] +[Int32] +$AngleSteps, +# Set the Radius_Steps of OBSCellShadedShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('ampFactor')] +[Single] +$AmpFactor, +# Set the notes of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'cell_shaded' +$ShaderNoun = 'OBSCellShadedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 5; +uniform int Radius_Steps< + string label = "Radius Steps"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 9; +uniform float ampFactor< + string label = "amp Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 2.0; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; + +float4 mainImage(VertData v_in) : TARGET +{ + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (3 * uv_pixel_interval.y); + float maxRadius = (24 * uv_pixel_interval.y); + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + for (float angle=0; angle <(2*PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps; + + } + } + accumulatedColor *= ampFactor; + + return c0 - accumulatedColor; // Cell shaded style +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6328,214 +7682,376 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPersistentData { - +function Get-OBSChromaticAberrationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] -[Alias('obs.powershell.websocket.GetPersistentData')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +# Set the power of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the gamma of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('gamma')] +[Single] +$Gamma, +# Set the num_iter of OBSChromaticAberrationShader +[Alias('num_iter')] +[ComponentModel.DefaultBindingProperty('num_iter')] +[Int32] +$NumIter, +# Set the distort_radial of OBSChromaticAberrationShader +[Alias('distort_radial')] +[ComponentModel.DefaultBindingProperty('distort_radial')] +[Management.Automation.SwitchParameter] +$DistortRadial, +# Set the distort_barrel of OBSChromaticAberrationShader +[Alias('distort_barrel')] +[ComponentModel.DefaultBindingProperty('distort_barrel')] +[Management.Automation.SwitchParameter] +$DistortBarrel, +# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader +[Alias('offset_spectrum_ycgco')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYcgco, +# Set the offset_spectrum_yuv of OBSChromaticAberrationShader +[Alias('offset_spectrum_yuv')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYuv, +# Set the use_random of OBSChromaticAberrationShader +[Alias('use_random')] +[ComponentModel.DefaultBindingProperty('use_random')] +[Management.Automation.SwitchParameter] +$UseRandom, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'chromatic-aberration' +$ShaderNoun = 'OBSChromaticAberrationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/XssGz8 +//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 +uniform float power< + string label = "Power"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 0.01; +uniform float gamma< + string label = "Gamma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 3.0; + float step = 0.01; +> = 2.2; +uniform int num_iter< + string label = "Number iterations"; + string widget_type = "slider"; + int minimum = 3; + int maximum = 25; + int step = 1; +> = 7; +uniform bool distort_radial = false; +uniform bool distort_barrel = false; +uniform bool offset_spectrum_ycgco = false; +uniform bool offset_spectrum_yuv = false; +uniform bool use_random = true; +float2 remap( float2 t, float2 a, float2 b ) { + return clamp( (t - a) / (b - a), 0.0, 1.0 ); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 spectrum_offset_rgb( float t ) +{ + float t0 = 3.0 * t - 1.5; + float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); + return ret; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float3 lin2srgb( float3 c ) +{ + return pow( c, float3(gamma, gamma, gamma) ); +} +float3 srgb2lin( float3 c ) +{ + return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float3 yCgCo2rgb(float3 ycc) +{ + float R = ycc.x - ycc.y + ycc.z; + float G = ycc.x + ycc.y; + float B = ycc.x - ycc.y - ycc.z; + return float3(R,G,B); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float3 spectrum_offset_ycgco( float t ) +{ + //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink + //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple + float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange + //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue + return yCgCo2rgb( ygo ); +} +float3 yuv2rgb( float3 yuv ) +{ + float3 rgb; + rgb.r = yuv.x + yuv.z * 1.13983; + rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); + rgb.b = yuv.x + yuv.y * 2.03211; + return rgb; } +float2 radialdistort(float2 coord, float2 amt) +{ + float2 cc = coord - 0.5; + return coord + 2.0 * cc * amt; +} -} +float2 barrelDistortion( float2 p, float2 amt ) +{ + p = 2.0 * p - 1.0; - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfile { + /* + const float maxBarrelPower = 5.0; + //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper + float theta = atan(p.y, p.x); + float2 radius = float2( length(p) ); + radius = pow(radius, 1.0 + maxBarrelPower * amt); + p.x = radius.x * cos(theta); + p.y = radius.y * sin(theta); + /*/ + // much faster version + //const float maxBarrelPower = 5.0; + //float radius = length(p); + float maxBarrelPower = sqrt(5.0); + float radius = dot(p,p); //faster but doesn''t match above accurately + p *= pow(float2(radius, radius), maxBarrelPower * amt); + /* */ -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] -[Alias('obs.powershell.websocket.GetProfileList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + return p * 0.5 + 0.5; +} + +float2 brownConradyDistortion(float2 uv, float dist) +{ + uv = uv * 2.0 - 1.0; + // positive values of K1 give barrel distortion, negative give pincushion + float barrelDistortion1 = 0.1 * dist; // K1 in text books + float barrelDistortion2 = -0.025 * dist; // K2 in text books + float r2 = dot(uv,uv); + uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; + //uv *= 1.0 + barrelDistortion1 * r2; + + // tangential distortion (due to off center lens elements) + // is not modeled in this function, but if it was, the terms would go here + return uv * 0.5 + 0.5; +} -process { +float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) +{ + float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); + //float2 dist = mix( min_distort, max_distort, t ); + if (distort_radial) + return radialdistort( uv, 2.0 * dist ); + + if(distort_barrel) + return barrelDistortion( uv, 1.75 * dist ); //distortion at center + return brownConradyDistortion( uv, 75.0 * dist.x ); +} +// ==== - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 spectrum_offset_yuv( float t ) +{ + //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green + //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green + float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange + //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue + return yuv2rgb( yuv ); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float3 spectrum_offset( float t ) +{ + if(offset_spectrum_ycgco) + return spectrum_offset_ycgco( t ); + if(offset_spectrum_yuv) + return spectrum_offset_yuv( t ); + return spectrum_offset_rgb( t ); + //return srgb2lin( spectrum_offset_rgb( t ) ); + //return lin2srgb( spectrum_offset_rgb( t ) ); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 max_distort = float2(power, power); + float2 min_distort = 0.5 * max_distort; + + float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); + + float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); + + //debug oversiz + //float2 distuv = distort( uv, 1.0, max_distort ); + //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) + //{ + // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; + //} + + + float stepsiz = 1.0 / (float(num_iter)-1.0); + float rnd = 0.0; + if(use_random) + rnd = rand_f; + + float t = rnd * stepsiz; + + float3 sumcol = float3(0.0, 0.0, 0.0); + float3 sumw = float3(0.0, 0.0, 0.0); + float colA = 0.0; + + for ( int i=0; iAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6544,111 +8060,205 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfileParameter { - +function Get-OBSChromaUVDistortionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] -[Alias('obs.powershell.websocket.GetProfileParameter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSChromaUVDistortionShader','Add-OBSChromaUVDistortionShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +# Set the distortion of OBSChromaUVDistortionShader +[ComponentModel.DefaultBindingProperty('distortion')] +[Single] +$Distortion, +# Set the amplitude of OBSChromaUVDistortionShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the chroma of OBSChromaUVDistortionShader +[ComponentModel.DefaultBindingProperty('chroma')] +[Single] +$Chroma, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Chroma+UV-Distortion' +$ShaderNoun = 'OBSChromaUVDistortionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/WsdyRN +//Higher values = less distortion +uniform float distortion< + string label = "Distortion"; + string widget_type = "slider"; + float minimum = 5.0; + float maximum = 1000.0; + float step = 0.01; +> = 75.; +//Higher values = tighter distortion +uniform float amplitude< + string label = "Amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 10.; +//Higher values = more color distortion +uniform float chroma< + string label = "Chroma"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 6.28318531; + float step = 0.01; +> = .5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float2 zoomUv(float2 uv, float zoom) { + float2 uv1 = uv; + uv1 += .5; + uv1 += zoom/2.-1.; + uv1 /= zoom; + return uv1; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uvt = v_in.uv; + + float2 uvtR = uvt; + float2 uvtG = uvt; + float2 uvtB = uvt; + + //Uncomment the following line to get varying chroma distortion + //chroma = sin(elapsed_time)/2.+.5; + + uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); + uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); + uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); + + float2 uvR = zoomUv(uvtR, 1.1); + float2 uvG = zoomUv(uvtG, 1.1); + float2 uvB = zoomUv(uvtB, 1.1); + + float4 colR = image.Sample(textureSampler, uvR); + float4 colG = image.Sample(textureSampler, uvG); + float4 colB = image.Sample(textureSampler, uvB); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6657,204 +8267,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordDirectory { - +function Get-OBSCircleMaskFilterShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] -[Alias('obs.powershell.websocket.GetRecordDirectory')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Radius of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Radius')] +[Single] +$Radius, +# Set the Circle_Offset_X of OBSCircleMaskFilterShader +[Alias('Circle_Offset_X')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] +[Int32] +$CircleOffsetX, +# Set the Circle_Offset_Y of OBSCircleMaskFilterShader +[Alias('Circle_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] +[Int32] +$CircleOffsetY, +# Set the Source_Offset_X of OBSCircleMaskFilterShader +[Alias('Source_Offset_X')] +[ComponentModel.DefaultBindingProperty('Source_Offset_X')] +[Int32] +$SourceOffsetX, +# Set the Source_Offset_Y of OBSCircleMaskFilterShader +[Alias('Source_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] +[Int32] +$SourceOffsetY, +# Set the Antialiasing of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Antialiasing')] +[Management.Automation.SwitchParameter] +$Antialiasing, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'circle-mask-filter' +$ShaderNoun = 'OBSCircleMaskFilterShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Circle Mask Filter version 1.01, for OBS Shaderfilter +// Copyright 2022 by SkeletonBow +// Twitter: +// Twitch: +// License: GNU GPLv2 +// +// Changelog: +// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. +// 1.0 - Initial release +uniform float Radius< + string label = "Radius"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform int Circle_Offset_X< + string label = "Circle Offset X"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Circle_Offset_Y< + string label = "Circle Offset X"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Source_Offset_X< + string label = "Source Offset X"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; +uniform int Source_Offset_Y< + string label = "Source Offset Y"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool Antialiasing = true; +#define Smoothness 100.00 +#define AAwidth 4 - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +#define uv_pi uv_pixel_interval - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; + float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + float radius = Radius * 0.01; + float smwidth = radius * Smoothness * 0.01; + + float4 obstex = image.Sample( textureSampler, uv - soffset); + float4 color = obstex; + // Account for aspect ratio + uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; + float2 cuv = 0.5 + coffset; + float dist = distance(cuv,uv); + // Anti-aliased or pixelated edge + if( Antialiasing ) { + color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); + } else { + color.a = step( dist, radius ); + } + + return float4(color.rgb, color.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } -} + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] -[Alias('obs.powershell.websocket.GetRecordStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6863,101 +8507,346 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSReplayBufferStatus { - +function Get-OBSClockAnalogShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] -[Alias('obs.powershell.websocket.GetReplayBufferStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the current_time_ms of OBSClockAnalogShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] +[Int32] +$CurrentTimeMs, +# Set the current_time_sec of OBSClockAnalogShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockAnalogShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockAnalogShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the hour_handle_color of OBSClockAnalogShader +[Alias('hour_handle_color')] +[ComponentModel.DefaultBindingProperty('hour_handle_color')] +[Single[]] +$HourHandleColor, +# Set the minute_handle_color of OBSClockAnalogShader +[Alias('minute_handle_color')] +[ComponentModel.DefaultBindingProperty('minute_handle_color')] +[Single[]] +$MinuteHandleColor, +# Set the second_handle_color of OBSClockAnalogShader +[Alias('second_handle_color')] +[ComponentModel.DefaultBindingProperty('second_handle_color')] +[Single[]] +$SecondHandleColor, +# Set the outline_color of OBSClockAnalogShader +[Alias('outline_color')] +[ComponentModel.DefaultBindingProperty('outline_color')] +[Single[]] +$OutlineColor, +# Set the top_line_color of OBSClockAnalogShader +[Alias('top_line_color')] +[ComponentModel.DefaultBindingProperty('top_line_color')] +[Single[]] +$TopLineColor, +# Set the background_color of OBSClockAnalogShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] +[Single[]] +$BackgroundColor, +# Set the time_offset_hours of OBSClockAnalogShader +[Alias('time_offset_hours')] +[ComponentModel.DefaultBindingProperty('time_offset_hours')] +[Int32] +$TimeOffsetHours, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'clock_analog' +$ShaderNoun = 'OBSClockAnalogShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Based on https://www.shadertoy.com/view/XdKXzy +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform float3 hour_handle_color = {1.0,1.0,1.0}; +uniform float3 minute_handle_color = {1.0,1.0,1.0}; +uniform float3 second_handle_color = {1.0,0.0,0.0}; +uniform float3 outline_color = {1.0,1.0,1.0}; +uniform float3 top_line_color = {1.0,0.0,0.0}; +uniform float3 background_color = {.5,.5,.5}; +uniform int time_offset_hours = 0; +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif +// this is my first try to actually use glsl almost from scratch +// so far all i''ve done is learning by doing / reading glsl docs. +// this is inspired by my non glsl „elapsed_time“ projects +// especially this one: https://www.gottz.de/analoguhr.htm - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// i will most likely use a buffer in future to calculate the elapsed_time +// aswell as to draw the background of the clock only once. +// tell me if thats a bad idea. - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// update: +// screenshot: http://i.imgur.com/dF0nHDk.png +// as soon as i think its in a usefull state i''ll release the source +// of that particular c++ application on github. +// i hope sommeone might find it usefull :D - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } +#define PI 3.141592653589793238462643383 + +// from https://www.shadertoy.com/view/4s3XDn <3 +float ln(float2 p, float2 a, float2 b) +{ + float2 pa = p - a; + float2 ba = b - a; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h); +} + +// i think i should spend some elapsed_time reading docs in order to minimize this. +// hints apreciated +// (Rotated LiNe) +float rln(float2 uv, float start, float end, float perc) { + float inp = perc * PI * 2.0; + float2 coord = float2(sin(inp), cos(inp)); + return ln(uv, coord * start, coord * end); +} + +// i need this to have an alphachannel in the output +// i intend to use an optimized version of this shader for a transparent desktop widget experiment +float4 mixer(float4 c1, float4 c2) { + // please tell me if you think this would boost performance. + // the elapsed_time i implemented mix myself it sure did reduce + // the amount of operations but i''m not sure now + // if (c2.a <= 0.0) return c1; + // if (c2.a >= 1.0) return c2; + return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); + // in case you are curious how you could implement mix yourself: + // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); +} + +float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { + if (dist <= width + shadow) { + // lets draw the shadow + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); + // now lets draw the antialiased handle + color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); + } + return color; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 R = uv_size; + // calculate the size of a pixel + float px = 1.0 / R.y; + // create percentages of the coordinate system + float2 p = (v_in.uv * uv_size).xy / R; + // center the scene and add perspective + float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); + + /*float2 uv = -1.0 + 2.0 * p.xy; + // lets add perspective for mobile device support + if (uv_size.x > uv_size.y) + uv.x *= uv_size.x / uv_size.y; + else + uv.y *= uv_size.y / uv_size.x;*/ + + // lets scale the scene a bit down: + uv *= 1.1; + px *= 0.9; + + float width = 0.015; + float dist = 1.0; + float centerdist = length(uv); + + float4 color = image.Sample(textureSampler, v_in.uv); + + // background of the clock + if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); + + float isRed = 1.0; + + if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { + // minute bars + for (float i = 0.0; i <= 15.0; i += 1.0) { + if (mod(i, 5.0) == 0.0) { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); + // draw first bar red + if (i == 0.0 && uv.y > 0.0) { + isRed = dist; + dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); + color = mixer(color, float4(top_line_color, dist)); + dist = 1.0; } } + else { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + // outline circle + dist = min(dist, abs(1.0-width-length(uv))); + // draw clock shadow + if (centerdist > 1.0) + color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); + + // draw outline + minute bars in white + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); + color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); + } + + if (centerdist < 1.0) { + float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); + // hour + color = styleHandle(color, px, + rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), + hour_handle_color, 0.03, 0.02); + + // minute + color = styleHandle(color, px, + rln(uv, -0.075, 0.7, elapsed_time / 3600.0), + minute_handle_color, 0.02, 0.02); + + // second + color = styleHandle(color, px, + min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), + second_handle_color, 0.01, 0.02); + } + + + return color; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -6966,204 +8855,381 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScene { - +function Get-OBSClockDigitalLedShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] -[Alias('obs.powershell.websocket.GetSceneList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# Set the current_time_sec of OBSClockDigitalLedShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalLedShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalLedShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the showMatrix of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showMatrix')] +[Management.Automation.SwitchParameter] +$ShowMatrix, +# Set the showOff of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showOff')] +[Management.Automation.SwitchParameter] +$ShowOff, +# Set the ampm of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ampm')] +[Management.Automation.SwitchParameter] +$Ampm, +# Set the ledColor of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ledColor')] +[String] +$LedColor, +# Set the offsetHours of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'clock_digital_led' +$ShaderNoun = 'OBSClockDigitalLedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// based on https://www.shadertoy.com/view/MdfGzf +// cmarangu has linked all 7 segments in his comments +// https://www.shadertoy.com/view/3dtSRj +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform bool showMatrix = false; +uniform bool showOff = false; +uniform bool ampm = false; +uniform float4 ledColor = {1.0,0,0,1.0}; +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; +float segment(float2 uv, bool On) +{ + if (!On && !showOff) + return 0.0; + + float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* + (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); + + //Fiddle with lights and matrix + //uv.x += sin(elapsed_time*60.0*6.26)/14.0; + //uv.y += cos(elapsed_time*60.0*6.26)/14.0; + + //led like brightness + if (On){ + seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; + } else { + seg *= -(0.05+length(uv*float2(0.2,0.1))); + } + return seg; } +float sevenSegment(float2 uv,int num) +{ + float seg= 0.0; + seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); + seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); + seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); + seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); + seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); + seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); + seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); + + return seg; +} -} +float showNum(float2 uv,int nr, bool zeroTrim) +{ + //Speed optimization, leave if pixel is not in segment + if (abs(uv.x)>1.5 || abs(uv.y)>1.2) + return 0.0; + + float seg= 0.0; + if (uv.x>0.0) + { + nr /= 10; + if (nr==0 && zeroTrim) + nr = -1; + seg += sevenSegment(uv+float2(-0.75,0.0),nr); + } else { + seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); + } - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneCollection { + return seg; +} +float dots(float2 uv) +{ + float seg = 0.0; + uv.y -= 0.5; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + uv.y += 1.0; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + return seg; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] -[Alias('obs.powershell.websocket.GetSceneCollectionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / + min(uv_size.x,uv_size.y); + + if (uv_size.x>uv_size.y) + { + uv *= 6.0; + } + else + { + uv *= 12.0; + } + + uv.x *= -1.0; + uv.x += uv.y/12.0; + //wobble + //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; + //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; + uv.x += 3.5; + float seg = 0.0; + if(timeMode == 0){ + seg += showNum(uv,current_time_sec,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + seg += showNum(uv,current_time_min,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + if (ampm) { + if(current_time_hour == 0){ + seg += showNum(uv,12,true); + }else if(current_time_hour > 12){ + seg += showNum(uv,current_time_hour-12,true); + }else{ + seg += showNum(uv,current_time_hour,true); + } + } else { + seg += showNum(uv,current_time_hour,true); + } + }else{ + float timeSecs = 0.0; + if(timeMode == 1){ + timeSecs = elapsed_time_enable; + }else if(timeMode == 2){ + timeSecs = elapsed_time_active; + }else if(timeMode == 3){ + timeSecs = elapsed_time_show; + }else if(timeMode == 4){ + timeSecs = elapsed_time_start; + } -process { + timeSecs += offsetSeconds + offsetHours*3600; + if(timeSecs < 0) + timeSecs = 0.9999-timeSecs; + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + + uv.x -= 1.75; + seg += dots(uv); + + uv.x -= 1.75; + + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + if (ampm) + { + if(timeSecs == 0.0){ + timeSecs = 12.0; + }else if (timeSecs > 12.0){ + timeSecs = mod(timeSecs,12.0); + } + }else if (timeSecs > 24.0) { + timeSecs = mod(timeSecs,24.0); + } + + uv.x -= 1.75; + + seg += dots(uv); + + uv.x -= 1.75; + seg += showNum(uv,int(mod(timeSecs,60.0)),true); + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + + if (seg==0.0){ + return image.Sample(textureSampler, v_in.uv); + } + // matrix over segment + if (showMatrix) + { + seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); + //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); + } + if (seg<0.0) + { + seg = -seg;; + return float4(seg,seg,seg,1.0); + } + return float4(ledColor.rgb * seg, ledColor.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -7172,473 +9238,658 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItem { - +function Get-OBSClockDigitalNixieShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] -[Alias('obs.powershell.websocket.GetSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +# Set the current_time_ms of OBSClockDigitalNixieShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] +[Int32] +$CurrentTimeMs, +# Set the current_time_sec of OBSClockDigitalNixieShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalNixieShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalNixieShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the offsetHours of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, +# Set the corecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('corecolor')] +[Single[]] +$Corecolor, +# Set the halocolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('halocolor')] +[Single[]] +$Halocolor, +# Set the flarecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('flarecolor')] +[Single[]] +$Flarecolor, +# Set the anodecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodecolor')] +[Single[]] +$Anodecolor, +# Set the anodehighlightscolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] +[Single[]] +$Anodehighlightscolor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'clock_digital_nixie' +$ShaderNoun = 'OBSClockDigitalNixieShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/fsBcRm +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +// Colors as named variables, if you want to tweak them +uniform float3 corecolor = {1.0,0.7,0.0}; +uniform float3 halocolor = {1.0,0.5,0.0}; +uniform float3 flarecolor = {1.0,0.3,0.0}; +uniform float3 anodecolor = {0.2,0.1,0.1}; +uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#define lessThan(a,b) (a < b) +#define greaterThan(a,b) (a > b) +#endif +// psrdnoise (c) Stefan Gustavson and Ian McEwan, +// ver. 2021-12-02, published under the MIT license: +// https://github.com/stegu/psrdnoise/ +float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) +{ + float2 uv = float2(x.x+x.y*0.5, x.y); + float2 i0 = floor(uv), f0 = frac(uv); + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + float2 i1 = i0 + o1, i2 = i0 + 1.0; + float2 v0 = float2(i0.x - i0.y*0.5, i0.y); + float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); + float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); + float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; + float3 iu, iv, xw, yw; + if(any(greaterThan(period, float2(0.0,0.0)))) { + xw = float3(v0.x, v1.x, v2.x); + yw = float3(v0.y, v1.y, v2.y); + if(period.x > 0.0) + xw = mod(float3(v0.x, v1.x, v2.x), period.x); + if(period.y > 0.0) + yw = mod(float3(v0.y, v1.y, v2.y), period.y); + iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); + } else { + iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); + } + float3 hash = mod(iu, 289.0); + hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); + hash = mod((hash*34.0 + 10.0)*hash, 289.0); + float3 psi = hash*0.07482 + alpha; + float3 gx = cos(psi); float3 gy = sin(psi); + float2 g0 = float2(gx.x, gy.x); + float2 g1 = float2(gx.y, gy.y); + float2 g2 = float2(gx.z, gy.z); + float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); + w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; + float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); + float n = dot(w4, gdotx); + float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; + float2 dn0 = w4.x*g0 + dw.x*x0; + float2 dn1 = w4.y*g1 + dw.y*x1; + float2 dn2 = w4.z*g2 + dw.z*x2; + gradient = 10.9*(dn0 + dn1 + dn2); + return 10.9*n; } +// Compute the shortest distance from p +// to a line segment from p1 to p2. +float lined(float2 p1, float2 p2, float2 p) { + float2 p1p2 = p2 - p1; + float2 v = normalize(p1p2); + float2 s = p - p1; + float t = dot(v, s); + if (t<0.0) return length(s); + if (t>length(p1p2)) return length(p - p2); + return length(s - t*v); +} -} +// Compute the shortest distance from p to a circle +// with center at c and radius r. (Extremely simple.) +float circled(float2 c, float r, float2 p) { + return abs(length(p - c) - r); +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemBlendMode { +// Compute the shortest distance from p to a +// circular arc with center c from p1 to p2. +// p1, p2 are in the +angle direction (ccw), +// to resolve the major/minor arc ambiguity, so +// specifying p1, p2 in the wrong order will +// yield the complement to the arc you wanted. +// If p1 = p2, the entire circle is drawn, but +// you don''t want to use this function to draw +// a circle. Use the simple circled() instead. +// If p1 and p2 have different distances to c, +// the end of the arc will not look right. If +// this is inconvenient, uncomment the 3rd line. +float arcd(float2 c, float2 p1, float2 p2, float2 p) { + float2 v1 = p1 - c; + float2 v2 = p2 - c; + // Optional: make sure p1, p2 are both on the circle + // v2 = normalize(v2)*length(v1); + float2 v = p - c; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi + if(all(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); // nearest end + } else { + return abs(length(v) - length(v1)); // dist to arc + } + } else { // Arc angle > pi + if(any(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); + } else { + return abs(length(v) - length(v1)); + } + } +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +// A convenient anti-aliased step() using auto derivatives +float aastep(float threshold, float value) { + float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); + return smoothstep(threshold-afwidth, threshold+afwidth, value); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// A smoothstep() that blends to an aastep() under minification +float aasmoothstep(float t1, float t2, float v) { + float aw = 0.7 * length(float2(ddx(v), ddy(v))); + float sw = max(0.5*(t2-t1), aw); + float st = 0.5*(t1+t2); + return smoothstep(st-sw, st+sw, v); +} +// Distance field of a hexagonal (simplex) grid +// The return vector contains the distances to the +// three closest points, sorted by magnitude. +float3 hexgrid(float2 p) { -process { + const float stretch = 1.0/0.8660; + + // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) + p.y = p.y * stretch; + // Transform to grid space (axis-aligned "simplex" grid) + float2 uv = float2(p.x + p.y*0.5, p.y); + // Determine which simplex we''re in, with i0 being the "base" + float2 i0 = floor(uv); + float2 f0 = frac(uv); + // o1 is the offset in simplex space to the second corner + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + // Enumerate the remaining simplex corners + float2 i1 = i0 + o1; + float2 i2 = i0 + float2(1.0, 1.0); + // Transform corners back to texture space + float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); + float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); + float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); + float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); + // Only three values - bubble sort is just fine. + d.yz = (d.y < d.z) ? d.yz : d.zy; + d.xy = (d.x < d.y) ? d.xy : d.yx; + d.yz = (d.y < d.z) ? d.yz : d.zy; + return d; +} +// The digits. Simple functions, only a lot of them. - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// These glyphs and their implementation as distance fields +// are the original work of me (stefan.gustavson@gmail.com), +// and the code below is released under the MIT license: +// https://opensource.org/licenses/MIT +// (If that is inconvenient for you, let me know. I''m reasonable.) +// +// Experts say mortals should not attempt to design character shapes. +// "It''s just ten simple digits", I thought, "How hard can it be?" +// A week later, after countless little tweaks to proportions and +// curvature, and with a notepad full of sketches and pen-and-paper +// math, some of it horribly wrong because it was decades since I +// solved this kind of equations by hand, I know the answer: +// It can be *really* hard. But also loads of fun! +// +float nixie0(float2 p) { + // Special hack instead of pasting together arcs and lines + float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); + return abs(d - 2.0); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float nixie1(float2 p) { + float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); + float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); + return min(d1, d2); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float nixie1alt(float2 p) { // Straight line + return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float nixie2(float2 p) { + const float x = 3.2368345; // Icky coordinates, + const float y = 4.4283002; // used twice below + float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); + float d2 = arcd(float2(10.657842, -5.001899), // Also icky + float2(x, y), float2(-0.25, 0.0), p); + float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float nixie2alt(float2 p) { // Straight neck + float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); + float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); + float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} +float nixie3(float2 p) { + // Two round parts: + // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); + // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); + // Angled top, more like classic Nixie tube digits: + float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); + float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); + return min(min(d1, d2), d3); } +float nixie3alt(float2 p) { // Two round parts of the same size + float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); + float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); + return min(d1, d2); +} -} +float nixie4(float2 p) { + // This digit is 5.0 units wide, most others are 4.0 or 4.5 + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); + return min(min(d1, d2), d3); +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemEnabled { +float nixie4alt(float2 p) { + // This digit is 4.0 units wide, but looks cropped + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(d1, d2), d3); +} +float nixie5(float2 p) { + float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); + float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); + float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); + float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + return min(min(d1, d2), min(d3, d4)); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] -[Alias('obs.powershell.websocket.GetSceneItemEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +float nixie5alt(float2 p) { + float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); + float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); + float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); + float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); + float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); + float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +float nixie6(float2 p) { + float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); + float d2 = circled(float2(2.0, 2.25), 2.25, p); + return min(d1, d2); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float nixie6alt(float2 p) { // Straight neck + float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); + float d2 = circled(float2(2.0,2.0), 2.0, p); + return min(d1, d2); +} +float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact + float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); + return min(min(d1, d2), d3); +} -process { +float nixie7alt(float2 p) { // Straight neck + float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); + return min(d1, d2); +} +float nixie8(float2 p) { + float d1 = circled(float2(2.0, 2.2), 2.2, p); + float d2 = circled(float2(2.0, 6.2), 1.8, p); + return min(d1, d2); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float nixie8alt(float2 p) { // Same size loops + float d1 = circled(float2(2.0,2.0), 2.0, p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float nixie9(float2 p) { + float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); + float d2 = circled(float2(2.0, 5.75), 2.25, p); + return min(d1, d2); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float nixie9alt(float2 p) { // Straight neck + float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float nixieminus(float2 p) { + return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float nixieequals(float2 p) { + float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); + float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); + return min(d1, d2); +} +float nixieplus(float2 p) { + float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); + float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); + return min(d1, d2); } +float nixiedot(float2 p) { + // circled with r=0 yields a point, but with more work + return length(p - float2(2.0, 0.0)); +} -} +float nixiecolon(float2 p) { + float d1 = length(p - float2(2.0,2.0)); + float d2 = length(p - float2(2.0, 5.0)); + return min(d1, d2); +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemId { +// End of MIT-licensed code +float number(int n, float2 p) { + switch(n) { + case 0: return nixie0(p); + case 1: return nixie1(p); + case 2: return nixie2(p); + case 3: return nixie3(p); + case 4: return nixie4(p); + case 5: return nixie5(p); + case 6: return nixie6(p); + case 7: return nixie7(p); + case 8: return nixie8(p); + case 9: return nixie9(p); + default: return 1e10; + } +} +// Display the current time with a retro Nixie-tube look +// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 +// All code in the "Image" tab is public domain. +// Functions in the "Common" tab are also public domain, +// except where a separate license is specified. +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = ((float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size)/uv_size.x); + float time = 0.0; + if(timeMode == 0){ + time = float(current_time_hour*3600+current_time_min*60+current_time_sec) + float(current_time_ms)/1000.0; + }else if(timeMode == 1){ + time = elapsed_time_enable; + }else if(timeMode == 2){ + time = elapsed_time_active; + }else if(timeMode == 3){ + time = elapsed_time_show; + }else if(timeMode == 4){ + time = elapsed_time_start; + } + time += offsetSeconds + offsetHours * 3600; + if(time < 0) + time = 0.9999-time; + float2 p = -3.0+uv*50.0 - float2(0.0,9.0); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] -[Alias('obs.powershell.websocket.GetSceneItemId')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), + max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + // Some relief for the GPU: exit early if we''re in the black margins + if(bbox == 0.0) { + return float4(float3(0.0,0.0,0.0),1.0); + } + + // If not, well, let''s put that GPU to good use! + float secs = floor(mod(time, 60.0)); + float mins = floor(mod(time, 3600.0)/60.0); + float hrs = floor(time/3600.0); + int h10 = int(floor(hrs/10.0)); + int h1 = int(floor(mod(hrs, 10.0))); + int m10 = int(floor(mins/10.0)); + int m1 = int(floor(mod(mins, 10.0))); + int s10 = int(floor(secs/10.0)); + int s1 = int(floor(mod(secs, 10.0))); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + float2 wspace = float2(6.5, 0.0); + float2 nspace = float2(3.5, 0.0); + float d = 1e10; + d = min(d, number(h10, p)); + d = min(d, number(h1, p-wspace)); + d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); + d = min(d, number(m10, p-2.0*wspace-nspace)); + d = min(d, number(m1, p-3.0*wspace-nspace)); + d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); + d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); + d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, + float2 g; // For gradients returned from psrdnoise() -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('searchOffset')] -[ValidateRange(-1,[int]::MaxValue)] -[double] -$SearchOffset, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Digit outlines + float core = 1.0-aastep(0.2, d); + // "flare" is a wide blurry region around the characters, and + // "flarenoise" is a spatio-temporal modulation of its extents + // (slight flickering, but not all characters at the same time) + float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); + float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); + flare *= flare; // A more rapid decline towards the edge + // "glow" is a variation in the intensity of the glowy cathode (core) + float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); + // Now we mess up the distance field a little for the "halo" effect + d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); + d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); + // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube + // doesn''t have this, but it adds some appealing visual detail. + // Looks more like hot filaments than "cold cathodes", but... oh, well. + float halo = 1.0-smoothstep(-0.3, 0.3, d); + // Brittle parameters! This scale/shift of p has a strong impact + // on the pattern at the edges of the grid through "anodefade". + float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); + float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders + // Fade the hexagonal holes in the anode towards the edges + float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), + max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); + float anode = 1.0 - aastep(0.1, anodedist - anodefade); -process { + float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); + float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know + float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black + mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); + mixcolor = lerp(mixcolor, halocolor, 0.9*halo); + mixcolor = lerp(mixcolor, corecolor, core*glow); + mixcolor = lerp(mixcolor, anodecolorresult, anode); + mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box + return float4(mixcolor,1.0); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -7647,117 +9898,172 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemIndex { - +function Get-OBSColorDepthShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] -[Alias('obs.powershell.websocket.GetSceneItemIndex')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the colorDepth of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('colorDepth')] +[Single] +$ColorDepth, +# Set the pixelSize of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('pixelSize')] +[Single] +$PixelSize, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'color-depth' +$ShaderNoun = 'OBSColorDepthShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/tscfWM +uniform float colorDepth< + string label = "Color depth"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 100.0; + float step = 0.01; +> = 5.0; +uniform float pixelSize< + string label = "Pixel Size"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.01; +> = 5.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + +float4 mainImage(VertData v_in) : TARGET +{ + // Change these to change results + float2 size = uv_size / pixelSize; + float2 uv = v_in.uv; + // Maps UV onto grid of variable size to pixilate the image + uv = round(uv*size)/size; + float4 col = image.Sample(textureSampler, uv); + // Maps color onto the specified color depth + return float4(round(col.r * colorDepth) / colorDepth, + round(col.g * colorDepth) / colorDepth, + round(col.b * colorDepth) / colorDepth, 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -7766,117 +10072,225 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemLocked { - +function Get-OBSColorGradeFilterShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] -[Alias('obs.powershell.websocket.GetSceneItemLocked')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSColorGradeFilterShader','Add-OBSColorGradeFilterShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the notes of OBSColorGradeFilterShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the lut of OBSColorGradeFilterShader +[ComponentModel.DefaultBindingProperty('lut')] +[String] +$Lut, +# Set the lut_amount_percent of OBSColorGradeFilterShader +[Alias('lut_amount_percent')] +[ComponentModel.DefaultBindingProperty('lut_amount_percent')] +[Int32] +$LutAmountPercent, +# Set the lut_scale_percent of OBSColorGradeFilterShader +[Alias('lut_scale_percent')] +[ComponentModel.DefaultBindingProperty('lut_scale_percent')] +[Int32] +$LutScalePercent, +# Set the lut_offset_percent of OBSColorGradeFilterShader +[Alias('lut_offset_percent')] +[ComponentModel.DefaultBindingProperty('lut_offset_percent')] +[Int32] +$LutOffsetPercent, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'color_grade_filter' +$ShaderNoun = 'OBSColorGradeFilterShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Color Grade Filter by Charles Fettinger for obs-shaderfilter plugin 4/2020 +//https://github.com/Oncorporation/obs-shaderfilter +//OBS messed up the LUT system, this is basically the old LUT system +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform string notes< + string widget_type = "info"; +> = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; +uniform texture2d lut< + string label = "LUT"; +>; +uniform int lut_amount_percent< + string label = "LUT amount percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 100; +uniform int lut_scale_percent< + string label = "LUT scale percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 100; +uniform int lut_offset_percent< + string label = "LUT offset percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); + float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); + float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + float4 textureColor = image.Sample(textureSampler, v_in.uv); + float lumaLevel = textureColor.r * 0.2126 + textureColor.g * 0.7152 + textureColor.b * 0.0722; + float blueColor = float(lumaLevel);//textureColor.b * 63.0 + + float2 quad1; + quad1.y = floor(floor(float(blueColor)) / 8.0); + quad1.x = floor(float(blueColor)) - (quad1.y * 8.0); + + float2 quad2; + quad2.y = floor(ceil(float(blueColor)) / 8.0); + quad2.x = ceil(float(blueColor)) - (quad2.y * 8.0); + + float2 texPos1; + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float2 texPos2; + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float4 newColor1 = lut.Sample(textureSampler, texPos1); + newColor1.rgb = newColor1.rgb * lut_scale + lut_offset; + float4 newColor2 = lut.Sample(textureSampler, texPos2); + newColor2.rgb = newColor2.rgb * lut_scale + lut_offset; + float4 luttedColor = lerp(newColor1, newColor2, frac(float(blueColor))); + + float4 final_color = lerp(textureColor, luttedColor, lut_amount); + return float4(final_color.rgb, textureColor.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -7885,236 +10299,358 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemSource { - +function Get-OBSCornerPinShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] -[Alias('obs.powershell.websocket.GetSceneItemSource')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCornerPinShader','Add-OBSCornerPinShader')] param( - +# Set the Antialias_Edges of OBSCornerPinShader +[Alias('Antialias_Edges')] +[ComponentModel.DefaultBindingProperty('Antialias_Edges')] +[Management.Automation.SwitchParameter] +$AntialiasEdges, +# Set the Top_Left_X of OBSCornerPinShader +[Alias('Top_Left_X')] +[ComponentModel.DefaultBindingProperty('Top_Left_X')] +[Single] +$TopLeftX, +# Set the Top_Left_Y of OBSCornerPinShader +[Alias('Top_Left_Y')] +[ComponentModel.DefaultBindingProperty('Top_Left_Y')] +[Single] +$TopLeftY, +# Set the Top_Right_X of OBSCornerPinShader +[Alias('Top_Right_X')] +[ComponentModel.DefaultBindingProperty('Top_Right_X')] +[Single] +$TopRightX, +# Set the Top_Right_Y of OBSCornerPinShader +[Alias('Top_Right_Y')] +[ComponentModel.DefaultBindingProperty('Top_Right_Y')] +[Single] +$TopRightY, +# Set the Bottom_Left_X of OBSCornerPinShader +[Alias('Bottom_Left_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] +[Single] +$BottomLeftX, +# Set the Bottom_Left_Y of OBSCornerPinShader +[Alias('Bottom_Left_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +[Single] +$BottomLeftY, +# Set the Bottom_Right_X of OBSCornerPinShader +[Alias('Bottom_Right_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +[Single] +$BottomRightX, +# Set the Bottom_Right_Y of OBSCornerPinShader +[Alias('Bottom_Right_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +[Single] +$BottomRightY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'corner-pin' +$ShaderNoun = 'OBSCornerPinShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Corner Pin, Version 0.1, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Based on: +// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm +// https://www.youtube.com/c/InigoQuilez +// https://iquilezles.org/ +// and the derivative StreamFX Corner Pin effect by Xaymar +// https://github.com/Xaymar/obs-StreamFX +// +// Description: +// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily +// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying +// image with proper perspective without distortion. +// +// TODO: +// - Add feature to automatically quantize corners to pixel centers +// +// Changelog: +// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by +// Inigo Quilez that it was based upon. +uniform bool Antialias_Edges = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Top_Left_X< + string label = "Top left x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Top_Left_Y< + string label = "Top left y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Top_Right_X< + string label = "Top right x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Top_Right_Y< + string label = "Top right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_X< + string label = "Bottom left x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_Y< + string label = "Bottom left y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom right x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// DEVELOPMENTAL DEBUGGING OPTIONS +//uniform float AAstrength = 1.0; +//uniform float AAdist = 1.0; +//uniform float debug_psmult = 1.0; +//#define PIXEL_SIZE_MULT 2.0 - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float cross2d(in float2 a, in float2 b) +{ + return (a.x * b.y) - (a.y * b.x); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) +{ + float2 result = float2(-1., -1.); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + 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); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemTransform { + 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); + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] -[Alias('obs.powershell.websocket.GetSceneItemTransform')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + return result; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +// distance to a line segment +float sdSegment( in float2 p, in float2 a, in float2 b ) +{ + p -= a; b -= a; + return length( p-b*saturate(dot(p,b)/dot(b,b)) ); +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) +float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) +{ + //float ps = 2.0 * (2.0/uv_size.y); // Original +// float ps = debug_psmult * (2.0/uv_size.y); + float ps = (2.0/uv_size.y); +// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now + return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage( VertData v_in ) : TARGET { + float2 p = 2.* v_in.uv - 1.; + + float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; + float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; + float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + // Convert from screen coords to potential Quad UV coordinates + float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); + if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { + return float4(0.0, 0.0, 0.0, 0.0); + } -process { + float4 texel = image.Sample(textureSampler, uv); + if ( Antialias_Edges ) { + // Anti-alias edges of texture + texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); + } + return texel; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8123,111 +10659,185 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneSceneTransitionOverride { - +function Get-OBSCrtCurvatureShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the strength of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the border of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('border')] +[String] +$Border, +# Set the feathering of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('feathering')] +[Single] +$Feathering, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'crt-curvature' +$ShaderNoun = 'OBSCrtCurvatureShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 200.; + float step = 0.01; +> = 33.33; +uniform float4 border< + string label = "Border Color"; +> = {0., 0., 0., 1.}; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float feathering< + string label = "Feathering"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.33; + + +float4 mainImage(VertData v_in) : TARGET +{ + float2 cc = v_in.uv - float2(0.5, 0.5); + float dist = dot(cc, cc) * strength / 100.0; + float2 bent = v_in.uv + cc * (1.0 + dist) * dist; + if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { + return border; + } + if (feathering >= .01) { + float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; + float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); + borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; + float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); + return lerp(border, image.Sample(textureSampler, bent), borderFade); + } + + return image.Sample(textureSampler, bent); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8236,101 +10846,234 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneTransition { - +function Get-OBSCubeRotatingShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] -[Alias('obs.powershell.websocket.GetSceneTransitionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the images of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('images')] +[Int32] +$Images, +# Set the speed of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the shadow of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('shadow')] +[Single] +$Shadow, +# Set the other_image1 of OBSCubeRotatingShader +[Alias('other_image1')] +[ComponentModel.DefaultBindingProperty('other_image1')] +[String] +$OtherImage1, +# Set the other_image2 of OBSCubeRotatingShader +[Alias('other_image2')] +[ComponentModel.DefaultBindingProperty('other_image2')] +[String] +$OtherImage2, +# Set the other_image3 of OBSCubeRotatingShader +[Alias('other_image3')] +[ComponentModel.DefaultBindingProperty('other_image3')] +[String] +$OtherImage3, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'cube_rotating' +$ShaderNoun = 'OBSCubeRotatingShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int images< + string label = "Images"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "1"; + int option_1_value = 2; + string option_1_label = "2"; + int option_2_value = 4; + string option_2_label = "4"; +> = 1; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float shadow< + string label = "Shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.5; + float step = 0.001; +> = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform texture2d other_image1; +uniform texture2d other_image2; +uniform texture2d other_image3; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +#define PI 3.14159265359 +float4 mainImage(VertData v_in) : TARGET +{ + float t = elapsed_time * speed; + float4 c = float4(0,0,0,0); + for(float side = 0.0; side<4.0; side += 1.0){ + float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; + float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; + if(left < right){ + float2 uv; + uv.x = (v_in.uv.x-left)/(right-left); + float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; + float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; + float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); + uv.y = (v_in.uv.y-0.5)*size+0.5; + float4 sample = float4(0,0,0,0); + if(images <= 1 || side == 0.0){ + sample = image.Sample(textureSampler, uv); + }else if(images == 2){ + if(side == 1.0 || side == 3.0){ + sample = other_image1.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + }else if(images == 4){ + if(side == 1.0){ + sample = other_image1.Sample(textureSampler, uv); + }else if(side == 2.0){ + sample = other_image2.Sample(textureSampler, uv); + }else if(side == 3.0){ + sample = other_image3.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + } + if(sample.a > 0.0){ + c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); + } + } + } + return c; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8339,229 +11082,187 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceActive { - +function Get-OBSCurveShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] -[Alias('obs.powershell.websocket.GetSourceActive')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] param( - +# Set the strength of OBSCurveShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the scale of OBSCurveShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the curve_color of OBSCurveShader +[Alias('curve_color')] +[ComponentModel.DefaultBindingProperty('curve_color')] +[String] +$CurveColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'curve' +$ShaderNoun = 'OBSCurveShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.14159265359 +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float4 curve_color = {0,0,0,0}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + const float ydiff = 1.0; + uv -= float2(0.5,ydiff); + uv.x *= ( uv_size.x / uv_size.y); + uv /= scale; + if(strength > 0.0){ + float d = tan((1.0-strength)*PI/2.0); + float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); + float2 center = float2(0.0,(1.0-ydiff)+d); + float pd = distance(uv, center); + if(pd < r) + return curve_color; + float angle = atan2(center.x-uv.x,center.y-uv.y); + uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); + } + uv.x /= ( uv_size.x / uv_size.y); + uv += float2(0.5,ydiff); + return image.Sample(textureSampler,uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilter { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] -[Alias('obs.powershell.websocket.GetSourceFilter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8570,106 +11271,354 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterDefaultSettings { - +function Get-OBSCutRectPerCornerShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] -[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, -# If set, will return the information that would otherwise be sent to OBS. +# Set the corner_tl of OBSCutRectPerCornerShader +[Alias('corner_tl')] +[ComponentModel.DefaultBindingProperty('corner_tl')] +[Int32] +$CornerTl, +# Set the corner_tr of OBSCutRectPerCornerShader +[Alias('corner_tr')] +[ComponentModel.DefaultBindingProperty('corner_tr')] +[Int32] +$CornerTr, +# Set the corner_br of OBSCutRectPerCornerShader +[Alias('corner_br')] +[ComponentModel.DefaultBindingProperty('corner_br')] +[Int32] +$CornerBr, +# Set the corner_bl of OBSCutRectPerCornerShader +[Alias('corner_bl')] +[ComponentModel.DefaultBindingProperty('corner_bl')] +[Int32] +$CornerBl, +# Set the border_thickness of OBSCutRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSCutRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSCutRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSCutRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSCutRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'cut_rect_per_corner' +$ShaderNoun = 'OBSCutRectPerCornerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_tl< + string label = "Corner top left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_tr< + string label = "Corner top right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_br< + string label = "Corner bottom right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_bl< + string label = "Corner bottom left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border aplha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float border_alpha_end< + string label = "Border aplha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; + int corner_right = corner_tr>corner_br?corner_tr:corner_br; + int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; + int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; + + if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = closedEdgeXabs+closedEdgeYabs; + if(d>corner_radius){ + if(d-corner_radius <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + return float4(0.0,0.0,0.0,0.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8678,101 +11627,192 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterKind { - +function Get-OBSCylinderShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] -[Alias('obs.powershell.websocket.GetSourceFilterKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the cylinder_factor of OBSCylinderShader +[Alias('cylinder_factor')] +[ComponentModel.DefaultBindingProperty('cylinder_factor')] +[Single] +$CylinderFactor, +# Set the background_cut of OBSCylinderShader +[Alias('background_cut')] +[ComponentModel.DefaultBindingProperty('background_cut')] +[Single] +$BackgroundCut, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'cylinder' +$ShaderNoun = 'OBSCylinderShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float cylinder_factor< + string label = "Cylinder factor"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.2; +uniform float background_cut< + string label = "Background cut"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.1; +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv.x -= 0.5; + float bend = sqrt(1.0 - uv.x*uv.x*4); + uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + uv.x += 0.5; + float4 front_color = image.Sample(textureSampler, uv); + front_color.rgb *= bend/2+0.5; + if(front_color.a >= 1.0) + return front_color; + + uv = v_in.uv; + uv.x -= 0.5; + if(abs(uv.x) < background_cut) + return front_color; + uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + if(uv.x > 0){ + uv.x = 1.0 - uv.x; + }else{ + uv.x = 0 - uv.x; + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 back_color = image.Sample(textureSampler, uv); + back_color.rgb *= 0.5-bend/2; + front_color.rgb *= front_color.a; + front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; + front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; + return front_color; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8781,111 +11821,161 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterList { - +function Get-OBSDarkenShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] -[Alias('obs.powershell.websocket.GetSourceFilterList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDarkenShader','Add-OBSDarkenShader')] param( - +# Set the Opacity_Percentage of OBSDarkenShader +[Alias('Opacity_Percentage')] +[ComponentModel.DefaultBindingProperty('Opacity_Percentage')] +[Single] +$OpacityPercentage, +# Set the Fill_Percentage of OBSDarkenShader +[Alias('Fill_Percentage')] +[ComponentModel.DefaultBindingProperty('Fill_Percentage')] +[Single] +$FillPercentage, +# Set the Notes of OBSDarkenShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'darken' +$ShaderNoun = 'OBSDarkenShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Opacity_Percentage = 100.0; +uniform float Fill_Percentage = 100.0; +uniform string Notes = "Simulates a photo editing darken layer blending mode. Fill percentage is the interior alpha and Opacity is the layer alpha."; +float4 mainImage(VertData v_in) : TARGET +{ + float4 other = float4(1.0, 1.0, 1.0, 1.0); + float4 base = image.Sample(textureSampler, v_in.uv); + float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); + float4 gray = float4(luminance,luminance,luminance, 1.0); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return min(base,other); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -8894,237 +11984,198 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceScreenshot { - +function Get-OBSDeadPixelFixerShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] -[Alias('obs.powershell.websocket.GetSourceScreenshot')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] param( - +# Set the Dead_Pixel_X of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_X')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] +[Int32] +$DeadPixelX, +# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_Y')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] +[Int32] +$DeadPixelY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'dead-pixel-fixer' +$ShaderNoun = 'OBSDeadPixelFixerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixel located at the user configured offset will have its color overridden by taking the average of the +// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. +// +// Changelog: +// 0.01 - Initial release +uniform int Dead_Pixel_X< + string label = "Dead Pixel X"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; +uniform int Dead_Pixel_Y< + string label = "Dead Pixel Y"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 blur_dead_pixel(in float2 pos) +{ + float3 color; + color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; + return color * 0.25; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; + float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); + float4 obstex = image.Sample(textureSampler, uv); + float3 color; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { + color = blur_dead_pixel(pos); + } else { + color.rgb = obstex.rgb; + } + return float4( color, obstex.a); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecialInputs { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] -[Alias('obs.powershell.websocket.GetSpecialInputs')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -9133,307 +12184,488 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStats { - +function Get-OBSDensitySatHueShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStats')] -[Alias('obs.powershell.websocket.GetStats')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# Set the notes of OBSDensitySatHueShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the density_r of OBSDensitySatHueShader +[Alias('density_r')] +[ComponentModel.DefaultBindingProperty('density_r')] +[Single] +$DensityR, +# Set the saturation_r of OBSDensitySatHueShader +[Alias('saturation_r')] +[ComponentModel.DefaultBindingProperty('saturation_r')] +[Single] +$SaturationR, +# Set the hueShift_r of OBSDensitySatHueShader +[Alias('hueShift_r')] +[ComponentModel.DefaultBindingProperty('hueShift_r')] +[Single] +$HueShiftR, +# Set the density_y of OBSDensitySatHueShader +[Alias('density_y')] +[ComponentModel.DefaultBindingProperty('density_y')] +[Single] +$DensityY, +# Set the saturation_y of OBSDensitySatHueShader +[Alias('saturation_y')] +[ComponentModel.DefaultBindingProperty('saturation_y')] +[Single] +$SaturationY, +# Set the hueShift_y of OBSDensitySatHueShader +[Alias('hueShift_y')] +[ComponentModel.DefaultBindingProperty('hueShift_y')] +[Single] +$HueShiftY, +# Set the density_g of OBSDensitySatHueShader +[Alias('density_g')] +[ComponentModel.DefaultBindingProperty('density_g')] +[Single] +$DensityG, +# Set the saturation_g of OBSDensitySatHueShader +[Alias('saturation_g')] +[ComponentModel.DefaultBindingProperty('saturation_g')] +[Single] +$SaturationG, +# Set the hueShift_g of OBSDensitySatHueShader +[Alias('hueShift_g')] +[ComponentModel.DefaultBindingProperty('hueShift_g')] +[Single] +$HueShiftG, +# Set the density_c of OBSDensitySatHueShader +[Alias('density_c')] +[ComponentModel.DefaultBindingProperty('density_c')] +[Single] +$DensityC, +# Set the saturation_c of OBSDensitySatHueShader +[Alias('saturation_c')] +[ComponentModel.DefaultBindingProperty('saturation_c')] +[Single] +$SaturationC, +# Set the hueShift_c of OBSDensitySatHueShader +[Alias('hueShift_c')] +[ComponentModel.DefaultBindingProperty('hueShift_c')] +[Single] +$HueShiftC, +# Set the density_b of OBSDensitySatHueShader +[Alias('density_b')] +[ComponentModel.DefaultBindingProperty('density_b')] +[Single] +$DensityB, +# Set the saturation_b of OBSDensitySatHueShader +[Alias('saturation_b')] +[ComponentModel.DefaultBindingProperty('saturation_b')] +[Single] +$SaturationB, +# Set the hueShift_b of OBSDensitySatHueShader +[Alias('hueShift_b')] +[ComponentModel.DefaultBindingProperty('hueShift_b')] +[Single] +$HueShiftB, +# Set the density_m of OBSDensitySatHueShader +[Alias('density_m')] +[ComponentModel.DefaultBindingProperty('density_m')] +[Single] +$DensityM, +# Set the saturation_m of OBSDensitySatHueShader +[Alias('saturation_m')] +[ComponentModel.DefaultBindingProperty('saturation_m')] +[Single] +$SaturationM, +# Set the hueShift_m of OBSDensitySatHueShader +[Alias('hueShift_m')] +[ComponentModel.DefaultBindingProperty('hueShift_m')] +[Single] +$HueShiftM, +# Set the global_density of OBSDensitySatHueShader +[Alias('global_density')] +[ComponentModel.DefaultBindingProperty('global_density')] +[Single] +$GlobalDensity, +# Set the global_saturation of OBSDensitySatHueShader +[Alias('global_saturation')] +[ComponentModel.DefaultBindingProperty('global_saturation')] +[Single] +$GlobalSaturation, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'density_sat_hue' +$ShaderNoun = 'OBSDensitySatHueShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Density-Saturation-Hue Shader for OBS Shaderfilter +// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 +uniform string notes< + string widget_type = "info"; +> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float density_r < + string label = "Red Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float saturation_r < + string label = "Red Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float hueShift_r < + string label = "Red Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float density_y < + string label = "Yellow Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float saturation_y < + string label = "Yellow Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -} +uniform float hueShift_y < + string label = "Yellow Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float density_g < + string label = "Green Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float saturation_g < + string label = "Green Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -} +uniform float hueShift_g < + string label = "Green Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamServiceSettings { +uniform float density_c < + string label = "Cyan Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float saturation_c < + string label = "Cyan Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] -[Alias('obs.powershell.websocket.GetStreamServiceSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform float hueShift_c < + string label = "Cyan Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float density_b < + string label = "Blue Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -process { +uniform float saturation_b < + string label = "Blue Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float hueShift_b < + string label = "Blue Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float density_m < + string label = "Magenta Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float saturation_m < + string label = "Magenta Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float hueShift_m < + string label = "Magenta Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +uniform float global_density < + string label = "Global Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float global_saturation < + string label = "Global Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +// Tetrahedral interpolation based on the ordering of the input color channels +float3 applyAdjustments(float3 p) { + // Pre-calculate the flipped density values for each channel: + float d_r = -(density_r + global_density); + float d_y = -(density_y + global_density); + float d_g = -(density_g + global_density); + float d_c = -(density_c + global_density); + float d_b = -(density_b + global_density); + float d_m = -(density_m + global_density); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + // Compute the color vectors for each hue region using the flipped densities: + float3 red = float3( + 1.0 + d_r, + d_r - (saturation_r + global_saturation), + d_r + hueShift_r - (saturation_r + global_saturation) + ); + + float3 yellow = float3( + 1.0 + hueShift_y + d_y, + 1.0 + d_y, + d_y - (saturation_y + global_saturation) + ); + + float3 green = float3( + d_g - (saturation_g + global_saturation), + 1.0 + d_g, + d_g + hueShift_g - (saturation_g + global_saturation) + ); + + float3 cyan = float3( + d_c - (saturation_c + global_saturation), + 1.0 + hueShift_c + d_c, + 1.0 + d_c + ); + + float3 blue = float3( + d_b + hueShift_b - (saturation_b + global_saturation), + d_b - (saturation_b + global_saturation), + 1.0 + d_b + ); + + float3 magenta = float3( + 1.0 + d_m, + d_m - (saturation_m + global_saturation), + 1.0 + hueShift_m + d_m + ); + + // Define the black and white endpoints: + float3 blk = float3(0.0, 0.0, 0.0); + float3 wht = float3(1.0, 1.0, 1.0); + + float3 rgb; + if (p.r > p.g) { + if (p.g > p.b) { + // p.r >= p.g >= p.b + rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); + } else if (p.r > p.b) { + // p.r >= p.b > p.g + rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); + } else { + // p.b >= p.r > p.g + rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; } - - if ($PassThru) { - [PSCustomObject]$requestPayload + } else { + if (p.b > p.g) { + // p.b >= p.g >= p.r + rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; + } else if (p.b > p.r) { + // p.g >= p.r and p.b > p.r + rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + // p.g >= p.b >= p.r + rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); } - + } + return clamp(rgb, 0.0, 1.0); } +float4 mainImage(VertData v_in) : TARGET { + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float3 adjustedColor = applyAdjustments(inputColor.rgb); + return float4(adjustedColor, inputColor.a); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] -[Alias('obs.powershell.websocket.GetStreamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -9442,307 +12674,242 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStudioModeEnabled { - +function Get-OBSDiffuseTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] -[Alias('obs.powershell.websocket.GetStudioModeEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the image_a of OBSDiffuseTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSDiffuseTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSDiffuseTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSDiffuseTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the num_samples of OBSDiffuseTransitionShader +[Alias('num_samples')] +[ComponentModel.DefaultBindingProperty('num_samples')] +[Int32] +$NumSamples, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'diffuse_transition' +$ShaderNoun = 'OBSDiffuseTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/4cK3z1 +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; +// Diffuse (move pixels) between two 2D images +// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) +// Note: the approach in this demo is different - rather than randomising paths we use means - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// increase for greater precision - this is O(n^2) :( +uniform int num_samples< + string label = "Number of samples (10)"; + string widget_type = "slider"; + int minimum = 2; + int maximum = 100; + int step = 1; +> = 10; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + if (transition_time < 0.00001) { + return image_a.Sample(textureSampler, uv); + } - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + // we need to normalise the distributions so just sum the samples for a division later + // note: could calculate this once per image in a buffer or something + float3 from_total = float3(0.0,0.0,0.0); + float3 to_total = float3(0.0,0.0,0.0); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + for (int i=0; iAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSVersion { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVersion')] -[Alias('obs.powershell.websocket.GetVersion')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -9751,204 +12918,232 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVideoSettings { - +function Get-OBSDigitalRainShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] -[Alias('obs.powershell.websocket.GetVideoSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDigitalRainShader','Add-OBSDigitalRainShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the font of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('font')] +[String] +$Font, +# Set the noise of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('noise')] +[String] +$Noise, +# Set the base_color of OBSDigitalRainShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] +[String] +$BaseColor, +# Set the rain_speed of OBSDigitalRainShader +[Alias('rain_speed')] +[ComponentModel.DefaultBindingProperty('rain_speed')] +[Single] +$RainSpeed, +# Set the char_speed of OBSDigitalRainShader +[Alias('char_speed')] +[ComponentModel.DefaultBindingProperty('char_speed')] +[Single] +$CharSpeed, +# Set the glow_contrast of OBSDigitalRainShader +[Alias('glow_contrast')] +[ComponentModel.DefaultBindingProperty('glow_contrast')] +[Single] +$GlowContrast, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'digital-rain' +$ShaderNoun = 'OBSDigitalRainShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// based on https://www.shadertoy.com/view/ldccW4 by WillKirkby - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +#ifndef OPENGL +#define fract frac +#define mix lerp +#endif - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform texture2d font = "font.png"; +uniform texture2d noise = "noise.png"; +uniform float4 base_color = {.1, 1.0, .35, 1.0}; +uniform float rain_speed< + string label = "Rain Speed"; + string widget_type = "slider"; + float minimum = 0.001; + float maximum = 2.0; + float step = .001; +> = 1.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float char_speed< + string label = "Character Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = .001; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float glow_contrast< + string label = "Glow contrast"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.5; + float step = .001; +> = 1.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float mod(float x, float y) +{ + return x - y * floor(x/y); } +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x/y); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSVirtualCamStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] -[Alias('obs.powershell.websocket.GetVirtualCamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { +float text(float2 fragCoord) +{ + float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; + float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; + uv = uv*.8+.1; // scale the letters up a bit + block += elapsed_time*.002*char_speed; + uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters + uv *= .0625; // bring back into 0-1 range + return font.Sample(textureSampler, uv).r; +} +float3 rain(float2 fragCoord) +{ + fragCoord.x -= mod(fragCoord.x, 16.); + float offset=sin(fragCoord.x*15.); + float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; + + float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); + return base_color.rgb / pow(y*20.0, glow_contrast); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + return mix(image.Sample(textureSampler, v_in.uv),float4(rain(float2(v_in.uv.x,1.0-v_in.uv.y)*uv_size),1.0), text(v_in.uv*uv_size)); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -9957,222 +13152,433 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputFiltersDialog { - +function Get-OBSDisplacementMapAdvancedInvertShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] -[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] +[Alias('Set-OBSDisplacementMapAdvancedInvertShader','Add-OBSDisplacementMapAdvancedInvertShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +# Set the displacement_info of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the displacement_curve of OBSDisplacementMapAdvancedInvertShader +[Alias('displacement_curve')] +[ComponentModel.DefaultBindingProperty('displacement_curve')] +[Int32] +$DisplacementCurve, +# Set the blur_info of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_info')] +[ComponentModel.DefaultBindingProperty('blur_info')] +[String] +$BlurInfo, +# Set the blur_size of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_size')] +[ComponentModel.DefaultBindingProperty('blur_size')] +[Single] +$BlurSize, +# Set the blur_quality of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_quality')] +[ComponentModel.DefaultBindingProperty('blur_quality')] +[Single] +$BlurQuality, +# Set the blur_directions of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_directions')] +[ComponentModel.DefaultBindingProperty('blur_directions')] +[Single] +$BlurDirections, +# Set the blur_angle of OBSDisplacementMapAdvancedInvertShader +[Alias('blur_angle')] +[ComponentModel.DefaultBindingProperty('blur_angle')] +[Single] +$BlurAngle, +# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedInvertShader +[Alias('chromatic_aberration_info')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] +[String] +$ChromaticAberrationInfo, +# Set the chromatic_aberration of OBSDisplacementMapAdvancedInvertShader +[Alias('chromatic_aberration')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration')] +[Single] +$ChromaticAberration, +# Set the colorize_info of OBSDisplacementMapAdvancedInvertShader +[Alias('colorize_info')] +[ComponentModel.DefaultBindingProperty('colorize_info')] +[String] +$ColorizeInfo, +# Set the colorize_color of OBSDisplacementMapAdvancedInvertShader +[Alias('colorize_color')] +[ComponentModel.DefaultBindingProperty('colorize_color')] +[String] +$ColorizeColor, +# Set the flags_info of OBSDisplacementMapAdvancedInvertShader +[Alias('flags_info')] +[ComponentModel.DefaultBindingProperty('flags_info')] +[String] +$FlagsInfo, +# Set the blue_affects_strength of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_strength')] +[ComponentModel.DefaultBindingProperty('blue_affects_strength')] +[Management.Automation.SwitchParameter] +$BlueAffectsStrength, +# Set the blue_affects_colorize of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_colorize')] +[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] +[Management.Automation.SwitchParameter] +$BlueAffectsColorize, +# Set the blue_affects_blur of OBSDisplacementMapAdvancedInvertShader +[Alias('blue_affects_blur')] +[ComponentModel.DefaultBindingProperty('blue_affects_blur')] +[Management.Automation.SwitchParameter] +$BlueAffectsBlur, +# Set the alpha_affects_strength of OBSDisplacementMapAdvancedInvertShader +[Alias('alpha_affects_strength')] +[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] +[Management.Automation.SwitchParameter] +$AlphaAffectsStrength, +# Set the apply_alpha of OBSDisplacementMapAdvancedInvertShader +[Alias('apply_alpha')] +[ComponentModel.DefaultBindingProperty('apply_alpha')] +[Management.Automation.SwitchParameter] +$ApplyAlpha, +# Set the background_layer of OBSDisplacementMapAdvancedInvertShader +[Alias('background_layer')] +[ComponentModel.DefaultBindingProperty('background_layer')] +[String] +$BackgroundLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + process { +$shaderName = 'displacement_map_advanced_invert' +$ShaderNoun = 'OBSDisplacementMapAdvancedInvertShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; +> = 0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) + +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; -} +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; -} +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputInteractDialog { +uniform texture2d background_layer < + string label = "Background Layer"; +>; +float4 mainImage(VertData v_in) : TARGET +{ + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] -[Alias('obs.powershell.websocket.OpenInputInteractDialog')] -param( + float4 map_rgba = image.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if (chromatic_aberration) { + float4 sc_r = background_layer.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_g = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_b = background_layer.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + + sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + } else { + sc = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + transparent += sc.a; + count++; + base_rgba += sc * sc.a; + samples += sc.a; + } + } + + //Calculate averages + if (samples > 0.0) + base_rgba /= samples; + + base_rgba.a = transparent / count; + } + + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + + if (apply_alpha) { + float4 background_rgba = background_layer.Sample(textureSampler, v_in.uv); + + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -10181,349 +13587,433 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputPropertiesDialog { +function Get-OBSDisplacementMapAdvancedShader { - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] -[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] +[Alias('Set-OBSDisplacementMapAdvancedShader','Add-OBSDisplacementMapAdvancedShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the displacement_info of OBSDisplacementMapAdvancedShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapAdvancedShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapAdvancedShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the displacement_curve of OBSDisplacementMapAdvancedShader +[Alias('displacement_curve')] +[ComponentModel.DefaultBindingProperty('displacement_curve')] +[Int32] +$DisplacementCurve, +# Set the blur_info of OBSDisplacementMapAdvancedShader +[Alias('blur_info')] +[ComponentModel.DefaultBindingProperty('blur_info')] +[String] +$BlurInfo, +# Set the blur_size of OBSDisplacementMapAdvancedShader +[Alias('blur_size')] +[ComponentModel.DefaultBindingProperty('blur_size')] +[Single] +$BlurSize, +# Set the blur_quality of OBSDisplacementMapAdvancedShader +[Alias('blur_quality')] +[ComponentModel.DefaultBindingProperty('blur_quality')] +[Single] +$BlurQuality, +# Set the blur_directions of OBSDisplacementMapAdvancedShader +[Alias('blur_directions')] +[ComponentModel.DefaultBindingProperty('blur_directions')] +[Single] +$BlurDirections, +# Set the blur_angle of OBSDisplacementMapAdvancedShader +[Alias('blur_angle')] +[ComponentModel.DefaultBindingProperty('blur_angle')] +[Single] +$BlurAngle, +# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration_info')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] +[String] +$ChromaticAberrationInfo, +# Set the chromatic_aberration of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration')] +[Single] +$ChromaticAberration, +# Set the colorize_info of OBSDisplacementMapAdvancedShader +[Alias('colorize_info')] +[ComponentModel.DefaultBindingProperty('colorize_info')] +[String] +$ColorizeInfo, +# Set the colorize_color of OBSDisplacementMapAdvancedShader +[Alias('colorize_color')] +[ComponentModel.DefaultBindingProperty('colorize_color')] +[String] +$ColorizeColor, +# Set the flags_info of OBSDisplacementMapAdvancedShader +[Alias('flags_info')] +[ComponentModel.DefaultBindingProperty('flags_info')] +[String] +$FlagsInfo, +# Set the blue_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_strength')] +[ComponentModel.DefaultBindingProperty('blue_affects_strength')] +[Management.Automation.SwitchParameter] +$BlueAffectsStrength, +# Set the blue_affects_colorize of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_colorize')] +[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] +[Management.Automation.SwitchParameter] +$BlueAffectsColorize, +# Set the blue_affects_blur of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_blur')] +[ComponentModel.DefaultBindingProperty('blue_affects_blur')] +[Management.Automation.SwitchParameter] +$BlueAffectsBlur, +# Set the alpha_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('alpha_affects_strength')] +[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] +[Management.Automation.SwitchParameter] +$AlphaAffectsStrength, +# Set the apply_alpha of OBSDisplacementMapAdvancedShader +[Alias('apply_alpha')] +[ComponentModel.DefaultBindingProperty('apply_alpha')] +[Management.Automation.SwitchParameter] +$ApplyAlpha, +# Set the mask_layer of OBSDisplacementMapAdvancedShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] +[String] +$MaskLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'displacement_map_advanced' +$ShaderNoun = 'OBSDisplacementMapAdvancedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; +> = 0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) + +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; -} +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; -} +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSSourceProjector { +uniform texture2d mask_layer < + string label = "Mask Layer"; +>; +float4 mainImage(VertData v_in) : TARGET +{ + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] -[Alias('obs.powershell.websocket.OpenSourceProjector')] -param( + float4 map_rgba = mask_layer.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + if (chromatic_aberration) { + float4 sc_r = image.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_g = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_b = image.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - if ($PassThru) { - [PSCustomObject]$requestPayload + sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + sc = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSVideoMixProjector { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] -[Alias('obs.powershell.websocket.OpenVideoMixProjector')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('videoMixType')] -[string] -$VideoMixType, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, + transparent += sc.a; + count++; + base_rgba += sc * sc.a; + samples += sc.a; + } + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('projectorGeometry')] -[string] -$ProjectorGeometry, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //Calculate averages + if (samples > 0.0) + base_rgba /= samples; + base_rgba.a = transparent / count; + } -process { + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + if (apply_alpha) { + float4 background_rgba = image.Sample(textureSampler, v_in.uv); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -10532,110 +14022,187 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSInput { - +function Get-OBSDisplacementMapInvertShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] -[Alias('obs.powershell.websocket.RemoveInput')] +[Alias('Set-OBSDisplacementMapInvertShader','Add-OBSDisplacementMapInvertShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the displacement_info of OBSDisplacementMapInvertShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapInvertShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapInvertShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the background_layer of OBSDisplacementMapInvertShader +[Alias('background_layer')] +[ComponentModel.DefaultBindingProperty('background_layer')] +[String] +$BackgroundLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'displacement_map_invert' +$ShaderNoun = 'OBSDisplacementMapInvertShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform texture2d background_layer ; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 map = image.Sample(textureSampler, v_in.uv); + float4 base = background_layer.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = background_layer.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -10644,105 +14211,187 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSProfile { +function Get-OBSDisplacementMapShader { - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] -[Alias('obs.powershell.websocket.RemoveProfile')] +[Alias('Set-OBSDisplacementMapShader','Add-OBSDisplacementMapShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the displacement_info of OBSDisplacementMapShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the mask_layer of OBSDisplacementMapShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] +[String] +$MaskLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'displacement_map' +$ShaderNoun = 'OBSDisplacementMapShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform texture2d mask_layer ; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 map = mask_layer.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = image.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -10751,228 +14400,236 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSScene { - +function Get-OBSDivideRotateShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] -[Alias('obs.powershell.websocket.RemoveScene')] +[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the iChannel0 of OBSDivideRotateShader +[ComponentModel.DefaultBindingProperty('iChannel0')] +[String] +$IChannel0, +# Set the speed_percentage of OBSDivideRotateShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSDivideRotateShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the notes of OBSDivideRotateShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'divide_rotate' +$ShaderNoun = 'OBSDivideRotateShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// divide and rotate shader for OBS Studio shaderfilter plugin +// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) +// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform texture2d iChannel0; +uniform int speed_percentage< + string label = "Speed"; + string widget_type = "slider"; + int minimum = -10; + int maximum = 10; + int step = 1; +> = 5; +uniform int alpha_percentage< + string label = "Opacity Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = true; +uniform string notes< + string widget_type = "info"; +> = "add rotation and speed"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float2 cm(float2 a, float2 b) { + return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); } +float2 iter(float2 uv, float2 rot, float scale) { + float2 gv = frac(cm(uv, rot) * scale); + float boundDist = 1. - max(abs(gv.x), abs(gv.y)); + float mask = step(.03, boundDist); + gv *= mask; + return gv; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSceneItem { +float4 mainImage(VertData v_in) : TARGET +{ + float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); + float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); + // Normalize coords + //float2 uv = (v_in.uv * uv_scale + uv_offset); + float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; + float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] -[Alias('obs.powershell.websocket.RemoveSceneItem')] -param( + // Add some time rotation and offset + float t = elapsed_time * speed; + float2 time = float2(sin(t), cos(t)); + uv += time; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + // Imaginary component has to be mirrored for natural feeling rotation + mouse.y *= -1.0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + // Draw few layers of this to bend space + float2 rot = cm(mouse, time); + for (float i=1.0; i<=3.0; i++) { + uv = iter(uv, rot, 1.5); + } -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Combine background with new image + float4 background_color = image.Sample(textureSampler, v_in.uv); + float4 col = iChannel0.Sample(textureSampler, uv); + // Border + if (uv.x == 0.0 && uv.y == 0.0) { + col = float4(0,0,0,alpha); + } -process { + // if not appling to alpha layer, set output alpha + if (Apply_To_Alpha_Layer == false) + col.a = alpha; + //output color is combined with background image + col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -10981,217 +14638,271 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSourceFilter { - +function Get-OBSDoodleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] -[Alias('obs.powershell.websocket.RemoveSourceFilter')] +[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +# Set the ViewProj of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSDoodleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSDoodleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSDoodleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSDoodleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSDoodleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSDoodleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the Doodle_Scale_Percent of OBSDoodleShader +[Alias('Doodle_Scale_Percent')] +[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] +[Single] +$DoodleScalePercent, +# Set the Snap_Percent of OBSDoodleShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] +[Single] +$SnapPercent, +# Set the Notes of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'doodle' +$ShaderNoun = 'OBSDoodleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float Doodle_Scale_Percent< + string label = "Doodle Scale Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 2.5; +uniform float Snap_Percent< + string label = "Snap Percent"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.1; +> = 7.5; +uniform string Notes< + string widget_type = "info"; +> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +float3 rand3(float3 co) +{ + float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = frac(512.0*j); + j *= .125; + result.x = frac(512.0*j); + j *= .125; + result.y = frac(512.0*j); + return result - 0.5; } +float snap(float x, float snap) +{ + return snap * round(x / max(0.01,snap)); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Resume-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] -[Alias('obs.powershell.websocket.ResumeRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); + float rand = snap(rand_f, Snap_Percent *.01); + float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); + vert_out.uv.xy += noise; + return vert_out; +} -process { +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} +technique Draw +{ + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -11200,248 +14911,276 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Save-OBSReplayBuffer { - +function Get-OBSDrawingsShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] -[Alias('obs.powershell.websocket.SaveReplayBuffer')] +[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the AngleNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('AngleNum')] +[Int32] +$AngleNum, +# Set the SampNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('SampNum')] +[Int32] +$SampNum, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'drawings' +$ShaderNoun = 'OBSDrawingsShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/ldlcWs +uniform int AngleNum< + string label = "Number of angles"; + string widget_type = "slider"; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 3; +uniform int SampNum< + string label = "Number of samples"; + string widget_type = "slider"; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 9; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 getCol(float2 pos) +{ + // take aspect ratio into account + float2 uv=pos; + float4 c1=image.Sample(textureSampler, uv); + float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); + c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); + float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); + float4 c2=float4(.7,.7,.7,.7); + return min(lerp(c1,c2,1.8*d),.7); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float4 getColHT(float2 pos) +{ + return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); +} +float getVal(float2 pos) +{ + float4 c=getCol(pos); + return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; } +float2 getGrad(float2 pos, float eps) +{ + float2 d=float2(eps,0.); + return float2( + getVal(pos+d.xy)-getVal(pos-d.xy), + getVal(pos+d.yx)-getVal(pos-d.yx) + )/eps/2.; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Save-OBSSourceScreenshot { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] -[Alias('obs.powershell.websocket.SaveSourceScreenshot')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFilePath')] -[string] -$ImageFilePath, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, + float lum( float3 c) { + return dot(c, float3(0.3, 0.59, 0.11)); + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float3 clipcolor( float3 c) { + float l = lum(c); + float n = min(min(c.r, c.g), c.b); + float x = max(max(c.r, c.g), c.b); + + if (n < 0.0) { + c.r = l + ((c.r - l) * l) / (l - n); + c.g = l + ((c.g - l) * l) / (l - n); + c.b = l + ((c.b - l) * l) / (l - n); + } + if (x > 1.25) { + c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); + c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); + c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); + } + return c; + } + float3 setlum( float3 c, float l) { + float d = l - lum(c); + c = c + float3(d,d,d); + return clipcolor(0.85*c); + } -process { +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + float3 col = float3(0,0,0); + float3 col2 = float3(0,0,0); + float sum=0.; + + for(int i=0;iAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - - Get-Item $paramCopy["imageFilePath"] | - Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | - Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru - } @@ -11449,223 +15188,218 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSCallVendorRequest { - +function Get-OBSDropShadowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] -[Alias('obs.powershell.websocket.CallVendorRequest')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSDropShadowShader','Add-OBSDropShadowShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('vendorName')] -[string] -$VendorName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestType')] -[string] -$RequestType, - +# Set the shadow_offset_x of OBSDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the notes of OBSDropShadowShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the shadow_color of OBSDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] +[String] +$ShadowColor, +# Set the is_alpha_premultiplied of OBSDropShadowShader +[Alias('is_alpha_premultiplied')] +[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] +[Management.Automation.SwitchParameter] +$IsAlphaPremultiplied, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestData')] -[PSObject] -$RequestData, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'drop_shadow' +$ShaderNoun = 'OBSDropShadowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Drop Shadow shader modified by Charles Fettinger +// impose a limiter to keep from crashing the system +//Converted to OpenGL by Exeldro February 19, 2022 +uniform int shadow_offset_x< + string label = "Shadow offset x"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_offset_y< + string label = "Shadow offset y"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_blur_size< + string label = "Shadow blur size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 15; + int step = 1; +> = 3; +uniform string notes< + string widget_type = "info"; +> = "blur size is limited to a max of 15 to ensure GPU"; +uniform float4 shadow_color; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool is_alpha_premultiplied; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); + int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); + + float sampled_shadow_alpha = 0.0; + + for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) + { + for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); } + } + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); + return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSCustomEvent { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] -[Alias('obs.powershell.websocket.BroadcastCustomEvent')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('eventData')] -[PSObject] -$EventData, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -11674,334 +15408,336 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSOffsetMediaInputCursor { - +function Get-OBSDrunkShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] -[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the color_matrix of OBSDrunkShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the glow_percent of OBSDrunkShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSDrunkShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSDrunkShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed_percent of OBSDrunkShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the Apply_To_Alpha_Layer of OBSDrunkShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the glow_color of OBSDrunkShader +[Alias('glow_color')] +[ComponentModel.DefaultBindingProperty('glow_color')] +[String] +$GlowColor, +# Set the ease of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the glitch of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] -[double] -$MediaCursorOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'drunk' +$ShaderNoun = 'OBSDrunkShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +uniform float4x4 color_matrix; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int glow_percent< + string label = "Glow percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform float4 glow_color; +uniform bool ease; +uniform bool glitch; +uniform string notes< + string widget_type = "info"; +> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +// Gaussian Blur +float Gaussian(float x, float o) { + const float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; +} +float BlurStyler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; } +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + } + return l_value; +} -} +float4 mainImage(VertData v_in) : TARGET +{ + float2 offsets[4]; + offsets[0] = float2(-0.05, 0.066); + offsets[1] = float2(-0.05, -0.066); + offsets[2] = float2(0.05, -0.066); + offsets[3] = float2(0.05, 0.066); - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSPauseRecord { + // convert input for vector math + float blur_amount = float(blur) /100; + float glow_amount = float(glow_percent) * 0.1; + float speed = float(pulse_speed_percent) * 0.01; + float luminance_floor = float(min_brightness) * 0.01; + float luminance_ceiling = float(max_brightness) * 0.01; + float4 color = image.Sample(textureSampler, v_in.uv); + float4 temp_color = color; + bool glitch_on = glitch; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] -[Alias('obs.powershell.websocket.PauseRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //circular easing variable + float t = 1 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); + float4 glitch_color = glow_color; -process { + for (int n = 0; n < 4; n++){ + //blur sample + b = BlurStyler(t,0,c,d,ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; + //test for rand_f color + if (glitch) { + glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); + if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) + { + glitch_on = true; + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance + ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color + glitch_on) //test for rand color + { + //glow calc + if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); + //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color + // max is used to simulate addition of vector texture color + temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time + max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time + * (glitch_color * (b / 2)); // glow color affected by glow over time + } + } + } + // grab lighter color + return max(color,temp_color); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSPressInputPropertiesButton { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] -[Alias('obs.powershell.websocket.PressInputPropertiesButton')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -12010,219 +15746,470 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSSleep { - +function Get-OBSDynamicMaskShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] -[Alias('obs.powershell.websocket.Sleep')] +[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepMillis')] -[ValidateRange(0,50000)] -[double] -$SleepMillis, - +# Set the input_source of OBSDynamicMaskShader +[Alias('input_source')] +[ComponentModel.DefaultBindingProperty('input_source')] +[String] +$InputSource, +# Set the red_base_value of OBSDynamicMaskShader +[Alias('red_base_value')] +[ComponentModel.DefaultBindingProperty('red_base_value')] +[Single] +$RedBaseValue, +# Set the red_red_input_value of OBSDynamicMaskShader +[Alias('red_red_input_value')] +[ComponentModel.DefaultBindingProperty('red_red_input_value')] +[Single] +$RedRedInputValue, +# Set the red_green_input_value of OBSDynamicMaskShader +[Alias('red_green_input_value')] +[ComponentModel.DefaultBindingProperty('red_green_input_value')] +[Single] +$RedGreenInputValue, +# Set the red_blue_input_value of OBSDynamicMaskShader +[Alias('red_blue_input_value')] +[ComponentModel.DefaultBindingProperty('red_blue_input_value')] +[Single] +$RedBlueInputValue, +# Set the red_alpha_input_value of OBSDynamicMaskShader +[Alias('red_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] +[Single] +$RedAlphaInputValue, +# Set the red_multiplier of OBSDynamicMaskShader +[Alias('red_multiplier')] +[ComponentModel.DefaultBindingProperty('red_multiplier')] +[Single] +$RedMultiplier, +# Set the green_base_value of OBSDynamicMaskShader +[Alias('green_base_value')] +[ComponentModel.DefaultBindingProperty('green_base_value')] +[Single] +$GreenBaseValue, +# Set the green_red_input_value of OBSDynamicMaskShader +[Alias('green_red_input_value')] +[ComponentModel.DefaultBindingProperty('green_red_input_value')] +[Single] +$GreenRedInputValue, +# Set the green_green_input_value of OBSDynamicMaskShader +[Alias('green_green_input_value')] +[ComponentModel.DefaultBindingProperty('green_green_input_value')] +[Single] +$GreenGreenInputValue, +# Set the green_blue_input_value of OBSDynamicMaskShader +[Alias('green_blue_input_value')] +[ComponentModel.DefaultBindingProperty('green_blue_input_value')] +[Single] +$GreenBlueInputValue, +# Set the green_alpha_input_value of OBSDynamicMaskShader +[Alias('green_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] +[Single] +$GreenAlphaInputValue, +# Set the green_multiplier of OBSDynamicMaskShader +[Alias('green_multiplier')] +[ComponentModel.DefaultBindingProperty('green_multiplier')] +[Single] +$GreenMultiplier, +# Set the blue_base_value of OBSDynamicMaskShader +[Alias('blue_base_value')] +[ComponentModel.DefaultBindingProperty('blue_base_value')] +[Single] +$BlueBaseValue, +# Set the blue_red_input_value of OBSDynamicMaskShader +[Alias('blue_red_input_value')] +[ComponentModel.DefaultBindingProperty('blue_red_input_value')] +[Single] +$BlueRedInputValue, +# Set the blue_green_input_value of OBSDynamicMaskShader +[Alias('blue_green_input_value')] +[ComponentModel.DefaultBindingProperty('blue_green_input_value')] +[Single] +$BlueGreenInputValue, +# Set the blue_blue_input_value of OBSDynamicMaskShader +[Alias('blue_blue_input_value')] +[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] +[Single] +$BlueBlueInputValue, +# Set the blue_alpha_input_value of OBSDynamicMaskShader +[Alias('blue_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] +[Single] +$BlueAlphaInputValue, +# Set the blue_multiplier of OBSDynamicMaskShader +[Alias('blue_multiplier')] +[ComponentModel.DefaultBindingProperty('blue_multiplier')] +[Single] +$BlueMultiplier, +# Set the alpha_base_value of OBSDynamicMaskShader +[Alias('alpha_base_value')] +[ComponentModel.DefaultBindingProperty('alpha_base_value')] +[Single] +$AlphaBaseValue, +# Set the alpha_red_input_value of OBSDynamicMaskShader +[Alias('alpha_red_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] +[Single] +$AlphaRedInputValue, +# Set the alpha_green_input_value of OBSDynamicMaskShader +[Alias('alpha_green_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] +[Single] +$AlphaGreenInputValue, +# Set the alpha_blue_input_value of OBSDynamicMaskShader +[Alias('alpha_blue_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] +[Single] +$AlphaBlueInputValue, +# Set the alpha_alpha_input_value of OBSDynamicMaskShader +[Alias('alpha_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] +[Single] +$AlphaAlphaInputValue, +# Set the alpha_multiplier of OBSDynamicMaskShader +[Alias('alpha_multiplier')] +[ComponentModel.DefaultBindingProperty('alpha_multiplier')] +[Single] +$AlphaMultiplier, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepFrames')] -[ValidateRange(0,10000)] -[double] -$SleepFrames, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'dynamic-mask' +$ShaderNoun = 'OBSDynamicMaskShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d input_source< + string label = "Input Source"; +>; +uniform float red_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float red_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSStreamCaption { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] -[Alias('obs.powershell.websocket.SendStreamCaption')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('captionText')] -[string] -$CaptionText, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform float green_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float green_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float blue_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float blue_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -process { +uniform float alpha_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float alpha_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +float4 mainImage(VertData v_in) : TARGET +{ + float4 input_color = input_source.Sample(textureSampler, v_in.uv); + float4 mask; + mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; + mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; + mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; + mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; + float4 base = image.Sample(textureSampler, v_in.uv); + return base * mask; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -12231,242 +16218,278 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByKeySequence { - +function Get-OBSEdgeDetectionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] -[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] +[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyId')] -[string] -$KeyId, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers')] -[PSObject] -$KeyModifiers, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] -[switch] -$KeyModifiersshift, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.control')] -[switch] -$KeyModifierscontrol, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] -[switch] -$KeyModifiersalt, - +# Set the sensitivity of OBSEdgeDetectionShader +[ComponentModel.DefaultBindingProperty('sensitivity')] +[Single] +$Sensitivity, +# Set the invert_edge of OBSEdgeDetectionShader +[Alias('invert_edge')] +[ComponentModel.DefaultBindingProperty('invert_edge')] +[Management.Automation.SwitchParameter] +$InvertEdge, +# Set the edge_color of OBSEdgeDetectionShader +[Alias('edge_color')] +[ComponentModel.DefaultBindingProperty('edge_color')] +[String] +$EdgeColor, +# Set the edge_multiply of OBSEdgeDetectionShader +[Alias('edge_multiply')] +[ComponentModel.DefaultBindingProperty('edge_multiply')] +[Management.Automation.SwitchParameter] +$EdgeMultiply, +# Set the non_edge_color of OBSEdgeDetectionShader +[Alias('non_edge_color')] +[ComponentModel.DefaultBindingProperty('non_edge_color')] +[String] +$NonEdgeColor, +# Set the non_edge_multiply of OBSEdgeDetectionShader +[Alias('non_edge_multiply')] +[ComponentModel.DefaultBindingProperty('non_edge_multiply')] +[Management.Automation.SwitchParameter] +$NonEdgeMultiply, +# Set the alpha_channel of OBSEdgeDetectionShader +[Alias('alpha_channel')] +[ComponentModel.DefaultBindingProperty('alpha_channel')] +[Management.Automation.SwitchParameter] +$AlphaChannel, +# Set the alpha_level of OBSEdgeDetectionShader +[Alias('alpha_level')] +[ComponentModel.DefaultBindingProperty('alpha_level')] +[Single] +$AlphaLevel, +# Set the alpha_invert of OBSEdgeDetectionShader +[Alias('alpha_invert')] +[ComponentModel.DefaultBindingProperty('alpha_invert')] +[Management.Automation.SwitchParameter] +$AlphaInvert, +# Set the rand_f of OBSEdgeDetectionShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the notes of OBSEdgeDetectionShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.command')] -[switch] -$KeyModifierscommand, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'edge_detection' +$ShaderNoun = 'OBSEdgeDetectionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Edge Detection for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform float sensitivity< + string label = "Sensitivity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.05; +uniform bool invert_edge; +uniform float4 edge_color = {1.0,1.0,1.0,1.0}; +uniform bool edge_multiply; +uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; +uniform bool non_edge_multiply; +uniform bool alpha_channel; +uniform float alpha_level< + string label = "Alpha level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform bool alpha_invert; +uniform float rand_f; +uniform string notes< + string widget_type = "info"; +> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + + float s = 3; + float hstep = uv_pixel_interval.x; + float vstep = uv_pixel_interval.y; + + float offsetx = (hstep * s) / 2.0; + float offsety = (vstep * s) / 2.0; + + float4 lum = float4(0.30, 0.59, 0.11, 1 ); + float samples[9]; + + int index = 0; + for(int i = 0; i < s; i++){ + for(int j = 0; j < s; j++){ + samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); + index++; + } + } + + float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; + float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; + float4 col; + + float o = ((vert * vert) + (hori * hori)); + bool isEdge = o > sensitivity; + if(invert_edge){ + isEdge = !isEdge; + } + if(isEdge) { + col = edge_color; + if(edge_multiply){ + col *= c; + } + } else { + col = non_edge_color; + if(non_edge_multiply){ + col *= c; + } + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + if (alpha_invert) { + lum = 1.0 - lum; + } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if(alpha_channel){ + if (edge_multiply && isEdge) { + return clamp(lerp(c, col, alpha_level), 0.0, 1.0); + } + else { + // use max instead of multiply + return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); + } + } else { + // col.a = col.a * alpha_level; + return col; + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByName { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] -[Alias('obs.powershell.websocket.TriggerHotkeyByName')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('hotkeyName')] -[string] -$HotkeyName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('contextName')] -[string] -$ContextName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -12475,436 +16498,599 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerMediaInputAction { - +function Get-OBSEmbersShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] -[Alias('obs.powershell.websocket.TriggerMediaInputAction')] +[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the ViewProj of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSEmbersShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSEmbersShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSEmbersShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSEmbersShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSEmbersShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSEmbersShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSEmbersShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSEmbersShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSEmbersShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the Animation_Speed of OBSEmbersShader +[Alias('Animation_Speed')] +[ComponentModel.DefaultBindingProperty('Animation_Speed')] +[Single] +$AnimationSpeed, +# Set the Movement_Direction_Horizontal of OBSEmbersShader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +[Single] +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSEmbersShader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Movement_Speed_Percent of OBSEmbersShader +[Alias('Movement_Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] +[Int32] +$MovementSpeedPercent, +# Set the Layers_Count of OBSEmbersShader +[Alias('Layers_Count')] +[ComponentModel.DefaultBindingProperty('Layers_Count')] +[Int32] +$LayersCount, +# Set the lumaMin of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Alpha_Percentage of OBSEmbersShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSEmbersShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaAction')] -[string] -$MediaAction, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'embers' +$ShaderNoun = 'OBSEmbersShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated +uniform float4x4 ViewProj; +uniform texture2d image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +uniform string notes< + string widget_type = "info"; +> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +#ifndef OPENGL +#define mat2 float2x2 +#define fract frac +#define mix lerp +#endif - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float Animation_Speed < + string label = "Animation Speed"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; + float scale = 1.; +> = 1.5; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 1.0; +> = 5.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 1.0; +> = 10.0; -} +uniform int Movement_Speed_Percent< + string label = "Movement Speed Percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 5; +uniform int Layers_Count < + string label = "Layers"; + string widget_type = "slider"; + int minimum = 1.0; + int maximum = 100.0; + int step = 1; +> = 15; +/* ps start +*/ -} - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerStudioModeTransition { +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform bool Apply_To_Alpha_Layer = true; +#define PI 3.1415927 +#define TWO_PI 6.283185 -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] -[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +#define PARTICLE_SIZE 0.009 +#define PARTICLE_SCALE float2(0.5, 1.6) +#define PARTICLE_SCALE_VAR float2(0.25, 0.2) -process { +#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) +#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) +#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 +#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 +#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +#define SIZE_MOD 1.05 +#define ALPHA_MOD 0.9 +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) +#define Movement_Speed Movement_Speed_Percent * 0.01 +#define UV float2(fragCoord.xy / uv_size) - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float hash1_2(float2 x) +{ + return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float2 hash2_2(float2 x) +{ + mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); + float2 y = mul(x, m); + return fract(sin(y) * 492.194); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +//Simple interpolated noise +float2 noise2_2(float2 uv) +{ + //float2 f = fract(uv); + float2 f = smoothstep(0.0, 1.0, fract(uv)); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + float2 v00 = hash2_2(uv00); + float2 v01 = hash2_2(uv01); + float2 v10 = hash2_2(uv10); + float2 v11 = hash2_2(uv11); + + float2 v0 = mix(v00, v01, f.y); + float2 v1 = mix(v10, v11, f.y); + float2 v = mix(v0, v1, f.x); + + return v; } +//Simple interpolated noise +float noise1_2(float2 uv) +{ + float2 f = fract(uv); + + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + + float v00 = hash1_2(uv00); + float v01 = hash1_2(uv01); + float v10 = hash1_2(uv10); + float v11 = hash1_2(uv11); + + float v0 = mix(v00, v01, f.y); + float v1 = mix(v10, v11, f.y); + float v = mix(v0, v1, f.x); + + return v; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentPreviewScene { +float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) +{ + float noise = 0.0; + float alpha = 1.0; + float size = 1.0; + float2 offset; + for (int i = 0; i < layers; i++) + { + offset += hash2_2(float2(alpha, size)) * 10.0; + + //Adding noise with movement + noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; + alpha *= alphaMod; + size *= sizeMod; + } + + noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); + return noise; +} +//Rotates point around 0,0 +float2 rotate(float2 vpoint, float deg) +{ + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + return mul(vpoint, m); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] -param( +//Cell center from point on the grid +float2 voronoiPointFromRoot(float2 root, float deg) +{ + float2 vpoint = hash2_2(root) - 0.5; + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + vpoint = mul(vpoint, m) * 0.66; + vpoint += root + 0.5; + return vpoint; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +//Voronoi cell point rotation degrees +float degFromRootUV(in float2 uv) +{ + return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) +{ + return vpoint + (hash2_2(uv) - 0.5) * range; +} -process { +float3 fireParticles(in float2 uv, in float2 originalUV) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float2 rootUV = floor(uv); + float deg = degFromRootUV(rootUV); + float2 pointUV = voronoiPointFromRoot(rootUV, deg); + float dist = 2.0; + float distBloom = 0.0; + + //UV manipulation for the faster particle movement + float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; + tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; + //Sparks sdf + dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); + + //Bloom sdf + distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + //Add sparks + particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; + + //Add bloom + particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + //Upper disappear curve randomization + float border = (hash1_2(rootUV) - 0.5) * 2.0; + float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); + + //Lower appear curve randomization + border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; + float appear = smoothstep(border, border + 0.4, originalUV.y); + + return particles * disappear * appear; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } +//Layering particles to imitate 3D view +float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float size = 1.0; + float alpha = 1.0; + float2 offset = float2(0.0, 0.0); + float2 noiseOffset; + float2 bokehUV; + + for (int i = 0; i < layers; i++) + { + //Particle noise movement + noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + //UV with applied movement + bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; + + //Adding particles if there is more smoke, remove smaller particles + particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); + + //Moving uv origin to avoid generating the same particles + offset += hash2_2(float2(alpha, alpha)) * 10.0; + + alpha *= alphaMod; + size *= sizeMod; + } - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + return particles; +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +void mainImage(out float4 fragColor, in float2 fragCoord) +{ + float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; + float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); + + uv *= 1.8; + float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); + + float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); + smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); + float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; + + //Cutting holes in smoke + smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), + 2.0) * 1.5; + + float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); + + float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); + col.rgb *= vignette; + col.rgb = smoothstep(-0.08, 1.0, col.rgb); + + if (Apply_To_Alpha_Layer) + { + float4 original_color = image.Sample(textureSampler, UV); + + float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min, 0.0, 1.0); + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } + + fragColor = col; } +/*ps end*/ -} +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProfile { +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] -[Alias('obs.powershell.websocket.SetCurrentProfile')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -12913,110 +17099,242 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProgramScene { - +function Get-OBSEmbossColorShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] -[Alias('obs.powershell.websocket.SetCurrentProgramScene')] +[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Angle_Steps of OBSEmbossColorShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] +[Int32] +$AngleSteps, +# Set the Radius_Steps of OBSEmbossColorShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('ampFactor')] +[Single] +$AmpFactor, +# Set the Up_Down_Percent of OBSEmbossColorShader +[Alias('Up_Down_Percent')] +[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] +[Int32] +$UpDownPercent, +# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the notes of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'emboss_color' +$ShaderNoun = 'OBSEmbossColorShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 9; // +uniform int Radius_Steps< + string label = "Radius Steps"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 4; // +uniform float ampFactor< + string label = "amp Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 12.0; +uniform int Up_Down_Percent< + string label = "Up Down Percent"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; +float4 mainImage(VertData v_in) : TARGET +{ + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (1 * uv_pixel_interval.y); + float maxRadius = (6 * uv_pixel_interval.y); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); + float embossAngle = 0.25 * PI; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) + { + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; + + for (float angle = 0; angle < (2 * PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; + + } + } + accumulatedColor *= ampFactor; + + c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); + } + //return c0 + accumulatedColor; // down; + //return c0 - accumulatedColor; // up + return c0; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13025,320 +17343,177 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneCollection { - +function Get-OBSEmbossShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] -[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] +[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Use_Color of OBSEmbossShader +[Alias('Use_Color')] +[ComponentModel.DefaultBindingProperty('Use_Color')] +[Management.Automation.SwitchParameter] +$UseColor, +# Set the Apply_To_Alpha_Layer of OBSEmbossShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransition { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'emboss' +$ShaderNoun = 'OBSEmbossShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform bool Use_Color; +uniform bool Apply_To_Alpha_Layer = true; +float4 mainImage(VertData v_in) : TARGET +{ - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float dx = 1 / uv_size.x; + float dy = 1 / uv_size.y; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 c0 = image.Sample(textureSampler, v_in.uv); + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); + float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); + float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); + float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); + float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); + float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + c0 = (-c1 - c2 - c4 + c6 + c8 + c9); + float c = (c0.r + c0.g + c0.b) / 3 + 0.5; + c0 = float4(c,c,c,c); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (Use_Color) + { + float4 rgba = image.Sample(textureSampler, v_in.uv); + return (0.5 * rgba) + c0; + } + } + return c0; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionDuration { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13347,110 +17522,300 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionSettings { - +function Get-OBSExeldroBentCameraShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] +[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionSettings')] -[PSObject] -$TransitionSettings, - +# Set the left_side_width of OBSExeldroBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] +[Single] +$LeftSideWidth, +# Set the left_side_size of OBSExeldroBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] +[Single] +$LeftSideSize, +# Set the left_side_shadow of OBSExeldroBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] +[Single] +$LeftSideShadow, +# Set the left_flip_width of OBSExeldroBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] +[Single] +$LeftFlipWidth, +# Set the left_flip_shadow of OBSExeldroBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] +[Single] +$LeftFlipShadow, +# Set the right_side_width of OBSExeldroBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] +[Single] +$RightSideWidth, +# Set the right_side_size of OBSExeldroBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] +[Single] +$RightSideSize, +# Set the right_side_shadow of OBSExeldroBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] +[Single] +$RightSideShadow, +# Set the right_flip_width of OBSExeldroBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] +[Single] +$RightFlipWidth, +# Set the right_flip_shadow of OBSExeldroBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam +$shaderName = 'exeldro-bent-camera' +$ShaderNoun = 'OBSExeldroBentCameraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float left_side_width< + string label = "Left side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float left_flip_shadow< + string label = "Left flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; + +uniform float right_side_width< + string label = "Right side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width / 2.0; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width / 2.0; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; + } + + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; + } + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13459,116 +17824,173 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioBalance { - +function Get-OBSFadeTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] -[Alias('obs.powershell.websocket.SetInputAudioBalance')] +[Alias('Set-OBSFadeTransitionShader','Add-OBSFadeTransitionShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the image_a of OBSFadeTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSFadeTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSFadeTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSFadeTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioBalance')] -[ValidateRange(0,1)] -[double] -$InputAudioBalance, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fade-transition' +$ShaderNoun = 'OBSFadeTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +float4 mainImage(VertData v_in) : TARGET +{ + float4 a_val = image_a.Sample(textureSampler, v_in.uv); + float4 b_val = image_b.Sample(textureSampler, v_in.uv); + float4 rgba = lerp(a_val, b_val, transition_time); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13577,115 +17999,230 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioMonitorType { - +function Get-OBSFillColorGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] +[Alias('Set-OBSFillColorGradientShader','Add-OBSFillColorGradientShader')] param( - +# Set the Fill of OBSFillColorGradientShader +[ComponentModel.DefaultBindingProperty('Fill')] +[Single] +$Fill, +# Set the Gradient_Width of OBSFillColorGradientShader +[Alias('Gradient_Width')] +[ComponentModel.DefaultBindingProperty('Gradient_Width')] +[Single] +$GradientWidth, +# Set the Gradient_Offset of OBSFillColorGradientShader +[Alias('Gradient_Offset')] +[ComponentModel.DefaultBindingProperty('Gradient_Offset')] +[Single] +$GradientOffset, +# Set the Fill_Direction of OBSFillColorGradientShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill_Color of OBSFillColorGradientShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorType')] -[string] -$MonitorType, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fill_color_gradient' +$ShaderNoun = 'OBSFillColorGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Fill< + string label = "Fill"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 1; + float step = 0.005; +> = 0.500; +uniform float Gradient_Width< + string label = "Gradient Width"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 0.15; // Adjust the maximum value as needed + float step = 0.01; +> = 0.05; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Gradient_Offset< + string label = "Gradient Offset"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 0.100; // Adjust the maximum value as needed + float step = 0.005; +> = 0.00; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int Fill_Direction< + string label = "Fill from:"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Left"; + int option_1_value = 1; + string option_1_label = "Right"; + int option_2_value = 2; + string option_2_label = "Bottom"; + int option_3_value = 3; + string option_3_label = "Top"; +> = 0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float4 Fill_Color; + +float4 mainImage(VertData v_in) : TARGET +{ + float distanceToEdge = 0.0; + + // Calculate distance to the fill edge based on the selected direction + if (Fill_Direction == 0) + distanceToEdge = Fill - v_in.uv.x; + else if (Fill_Direction == 1) + distanceToEdge = v_in.uv.x - (1.0 - Fill); + else if (Fill_Direction == 2) + distanceToEdge = v_in.uv.y - (1.0 - Fill); + else if (Fill_Direction == 3) + distanceToEdge = Fill - v_in.uv.y; + + // Calculate the gradient factor based on the distance to the edge and the gradient width + float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); + float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; + + // Adjust distanceToEdge by the Gradient_Offset + distanceToEdge += gradientOffset; + + // Normalize the distance to be between 0 and 1 + distanceToEdge = saturate(distanceToEdge); + + // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); + // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); + float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); + + // Blend between the fill color and the original image color using the gradient factor + float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); + + return finalColor; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13694,116 +18231,195 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioSyncOffset { - +function Get-OBSFillColorLinearShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] +[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Fill of OBSFillColorLinearShader +[ComponentModel.DefaultBindingProperty('Fill')] +[Single] +$Fill, +# Set the Fill_Direction of OBSFillColorLinearShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill_Color of OBSFillColorLinearShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] -[ValidateRange(-950,20000)] -[double] -$InputAudioSyncOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fill_color_linear' +$ShaderNoun = 'OBSFillColorLinearShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Fill< + string label = "Fill"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 1; + float step = 0.005; +>; +uniform int Fill_Direction< + string label = "Fill from:"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Left"; + int option_1_value = 1; + string option_1_label = "Right"; + int option_2_value = 2; + string option_2_label = "Top"; + int option_3_value = 3; + string option_3_label = "Bottom"; +> = 0; +uniform float4 Fill_Color; +float4 mainImage(VertData v_in) : TARGET +{ + bool is_inside_fill = true; + + // Check if the pixel is within the specified "fill width" on the left side + if(Fill_Direction == 0){ + is_inside_fill = v_in.uv.x > Fill; + } + if(Fill_Direction == 1) + { + is_inside_fill = v_in.uv.x < (1.0 - Fill); + } + if(Fill_Direction == 2) + { + is_inside_fill = v_in.uv.y > Fill; + } + if(Fill_Direction == 3) + { + is_inside_fill = v_in.uv.y < (1.0 - Fill); + } + + // Invert is_inside_fill + is_inside_fill = !is_inside_fill; + + // If inside the "fill," make the pixel selected colour; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -13812,232 +18428,249 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioTracks { - +function Get-OBSFillColorRadialDegreesShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] -[Alias('obs.powershell.websocket.SetInputAudioTracks')] +[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Fill_Direction of OBSFillColorRadialDegreesShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill of OBSFillColorRadialDegreesShader +[ComponentModel.DefaultBindingProperty('Fill')] +[Single] +$Fill, +# Set the Start_Angle of OBSFillColorRadialDegreesShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] +[Single] +$StartAngle, +# Set the Offset_X of OBSFillColorRadialDegreesShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialDegreesShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialDegreesShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioTracks')] -[PSObject] -$InputAudioTracks, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fill_color_radial_degrees' +$ShaderNoun = 'OBSFillColorRadialDegreesShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.141592653589793238 +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float Fill< + string label = "Fill"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.00000; +>; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float Start_Angle< + string label = "Start Angle"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 720; + float step = 1.00000; +> = 360.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -} +uniform float Offset_Y< + string label = "Offset Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float4 Fill_Color; -} +float4 mainImage(VertData v_in) : TARGET +{ + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputMute { + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] -[Alias('obs.powershell.websocket.SetInputMute')] -param( + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + // Convert angle from radians to degrees + angle = degrees(angle); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputMuted')] -[switch] -$InputMuted, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; + } + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; -process { + // Check if the angle is within the specified "fill width" + bool is_inside_fill = angle < Fill; + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -14046,237 +18679,252 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputName { - +function Get-OBSFillColorRadialPercentageShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] -[Alias('obs.powershell.websocket.SetInputName')] +[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Fill_Direction of OBSFillColorRadialPercentageShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill of OBSFillColorRadialPercentageShader +[ComponentModel.DefaultBindingProperty('Fill')] +[Single] +$Fill, +# Set the Start_Angle of OBSFillColorRadialPercentageShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] +[Single] +$StartAngle, +# Set the Offset_X of OBSFillColorRadialPercentageShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialPercentageShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialPercentageShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newInputName')] -[string] -$NewInputName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fill_color_radial_percentage' +$ShaderNoun = 'OBSFillColorRadialPercentageShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.141592653589793238 +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Fill< + string label = "Fill"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.00005; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float Start_Angle< + string label = "Start Angle"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 720; + float step = 1.00000; +> = 360.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -} +uniform float Offset_Y< + string label = "Offset Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float4 Fill_Color; -} +float4 mainImage(VertData v_in) : TARGET +{ + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputSettings { + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] -[Alias('obs.powershell.websocket.SetInputSettings')] -param( + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + // Convert angle from radians to degrees + angle = degrees(angle); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; + // Calculate the percentage of the angle + float anglePercentage = angle / 360.0; -process { + // Check if the angle percentage is within the specified "fill percentage" + bool is_inside_fill = anglePercentage < Fill; + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -14285,352 +18933,259 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputVolume { - +function Get-OBSFilterTemplateShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] -[Alias('obs.powershell.websocket.SetInputVolume')] +[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeMul')] -[ValidateRange(0,20)] -[double] -$InputVolumeMul, - +# Set the ViewProj of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSFilterTemplateShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSFilterTemplateShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSFilterTemplateShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSFilterTemplateShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSFilterTemplateShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSFilterTemplateShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSFilterTemplateShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSFilterTemplateShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSFilterTemplateShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeDb')] -[ValidateRange(-100,26)] -[double] -$InputVolumeDb, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'filter_template' +$ShaderNoun = 'OBSFilterTemplateShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//My shader modified by Me for use with obs-shaderfilter month/year v.02 +//Section to converting GLSL to HLSL - can delete if unneeded +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float4x4 ViewProj; +uniform texture2d image; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ +uniform string notes< + string widget_type = "info"; +> = "add notes here"; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); } +/* +**Shaders use the built in Draw technique** +**this section can be deleted** -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaInputCursor { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] -[Alias('obs.powershell.websocket.SetMediaInputCursor')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursor')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$MediaCursor, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} +*/ - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSOutputSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] -[Alias('obs.powershell.websocket.SetOutputSettings')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputSettings')] -[PSObject] -$OutputSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -14639,585 +19194,699 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSPersistentData { - +function Get-OBSFire3Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] -[Alias('obs.powershell.websocket.SetPersistentData')] +[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotValue')] -[PSObject] -$SlotValue, -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSFire3Shader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSFire3Shader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSFire3Shader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSFire3Shader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSFire3Shader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSFire3Shader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSFire3Shader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSFire3Shader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSFire3Shader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the Movement_Direction_Horizontal of OBSFire3Shader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +[Single] +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSFire3Shader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Alpha_Percentage of OBSFire3Shader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Invert of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFire3Shader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFire3Shader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Color_To_Replace of OBSFire3Shader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Apply_To_Specific_Color of OBSFire3Shader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Full_Width of OBSFire3Shader +[Alias('Full_Width')] +[ComponentModel.DefaultBindingProperty('Full_Width')] +[Management.Automation.SwitchParameter] +$FullWidth, +# Set the Flame_Size of OBSFire3Shader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Single] +$FlameSize, +# Set the Spark_Grid_Height of OBSFire3Shader +[Alias('Spark_Grid_Height')] +[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] +[Single] +$SparkGridHeight, +# Set the Flame_Modifier of OBSFire3Shader +[Alias('Flame_Modifier')] +[ComponentModel.DefaultBindingProperty('Flame_Modifier')] +[Single] +$FlameModifier, +# Set the Flame_Tongue_Size of OBSFire3Shader +[Alias('Flame_Tongue_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] +[Single] +$FlameTongueSize, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fire-3' +$ShaderNoun = 'OBSFire3Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//My effect modified by Me for use with obs-shaderfilter month/year v.02 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) -} +uniform int Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 80; +uniform bool Invert = false; +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.04; +uniform bool Apply_To_Image = true; +uniform bool Replace_Image_Color = true; +uniform float4 Color_To_Replace; +uniform bool Apply_To_Specific_Color = false; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -} +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSProfileParameter { +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float2 uv = v_in.uv; + if(Invert) + uv = 1.0 - v_in.uv; + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} +int2 iMouse() +{ + return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] -[Alias('obs.powershell.websocket.SetProfileParameter')] -param( -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, +float mod(float x, float y) +{ + return x - y * floor(x / y); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x / y); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterValue')] -[string] -$ParameterValue, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +/*ps start*/ +#define PI 3.1415926535897932384626433832795 +uniform bool Full_Width = false; +uniform float Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; -process { +uniform float Spark_Grid_Height< + string label = "Spark Grid Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float Flame_Modifier< + string label = "Flame Modifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Flame_Tongue_Size< + string label = "Flame Tongue Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 8.5; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// +// Description : Array and textureless GLSL 2D/3D/4D simplex +// noise functions. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : ijm +// Lastmod : 20110822 (ijm) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. +// https://github.com/ashima/webgl-noise +// - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +vec3 mod2893(vec3 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +vec4 mod289(vec4 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +vec4 permute(vec4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} +vec4 taylorInvSqrt(vec4 r) +{ + return 1.79284291400159 - 0.85373472095314 * r; } +float snoise(vec3 v) +{ + const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); -} +// First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSRecordDirectory { +// Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] -[Alias('obs.powershell.websocket.SetRecordDirectory')] -param( +// Permutations + i = mod2893(i); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('recordDirectory')] -[string] -$RecordDirectory, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// Gradients: 7x7 points over a square, mapped onto an octahedron. +// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) -process { + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +//Normalise gradients + //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; +// Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); } +////////////////////////////////////////////////////////////// -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemBlendMode { - +// PRNG +// From https://www.shadertoy.com/view/4djSRW +float prng(in vec2 seed) +{ + seed = fract(seed * vec2(5.3983, 5.4427)); + seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); + return fract(seed.x * seed.y * 95.4337); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] -param( +////////////////////////////////////////////////////////////// -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +float noiseStack(vec3 pos, int octaves, float falloff) +{ + float noise = snoise(vec3(pos)); + float off = 1.0; + if (octaves > 1) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 2) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 3) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + return (1.0 + noise) / 2.0; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) +{ + float displaceA = noiseStack(pos, octaves, falloff); + float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); + return vec2(displaceA, displaceB); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] -[string] -$SceneItemBlendMode, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float2 UV = (1.0 - v_in.uv) * uv_scale; + if (Invert) + UV = v_in.uv * uv_scale; + float alpha = saturate(Alpha_Percentage * .01); + float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); + + vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); + if (Full_Width) + { + resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; + + } + resolution.x = mul(resolution.x, 1 / 1); + float time = iTime * (Speed * 0.01); + //vec2 drag = iMouse().xy; + vec2 offset = iMouse().xy; + // + float xpart = UV.x / resolution.x; + float ypart = UV.y / resolution.y; + // + + float ypartClip = UV.y / ( flame_size * 75.0); + float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); + float ypartClipped = min(ypartClip, 1.0); + float ypartClippedn = (1 - ypartClipped); + // + float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); + // + float timeSpeed = 0.5 * (Speed * 0.01); + float realTime = -1.0 * timeSpeed * time; + // + vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; + vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); + vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); + vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; + // + vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); + vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); + // + vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; + float noise = noiseStack(noiseCoord, 3, 0.4); + // + float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); + // + float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); + float fff = f * f * f; + vec3 fire = 1.5 * vec3(f, fff, fff * fff); + // + // smoke + float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; + float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); + vec3 smoke = vec3(smokePart, smokePart, smokePart); + // + // sparks + float sparkGridSize = Spark_Grid_Height; + vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); + sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); + sparkCoord += 100.0 * flow.xy; + if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) + sparkCoord.x += 0.5 * sparkGridSize; + vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); + float sparkRandom = prng( sparkGridIndex); + float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); + vec3 sparks = vec3(0.0, 0.0, 0.0); + if (sparkLife > 0.0) + { + float sparkSize = xfuel * xfuel * sparkRandom * 0.08; + float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; + vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); + vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; + vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); + float sparkLength = length(sparkModulus); + float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); + sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); + } + // + float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); + + // remove dark areas per user + float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire, 0.0, alpha); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + else + { + rgba = color; } + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + + return rgba; +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemEnabled { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] -[Alias('obs.powershell.websocket.SetSceneItemEnabled')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -15226,245 +19895,434 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemIndex { - +function Get-OBSFireShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] -[Alias('obs.powershell.websocket.SetSceneItemIndex')] +[Alias('Set-OBSFireShader','Add-OBSFireShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Alpha_Percentage of OBSFireShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFireShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Flame_Size of OBSFireShader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Int32] +$FlameSize, +# Set the Fire_Type of OBSFireShader +[Alias('Fire_Type')] +[ComponentModel.DefaultBindingProperty('Fire_Type')] +[Int32] +$FireType, +# Set the Invert of OBSFireShader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFireShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFireShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSFireShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSFireShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSFireShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemIndex, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fire' +$ShaderNoun = 'OBSFireShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 +// https://github.com/Oncorporation/obs-shaderfilter plugin +// https://www.shadertoy.com/view/MtcGD7 original version +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +//v.5 +// flicker +// flame type +// apply to image +// replace image color +// speed +// flame size +// alpha +// invert direction/position - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform int Alpha_Percentage< + string label = "Aplha Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 100; +uniform int Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 70; +uniform int Fire_Type< + string label = "Fire Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Smaller and more whisps"; + int option_1_value = 1; + string option_1_label = "Larger and more volume"; +> = 1; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform bool Invert < + string name = "Invert"; +> = false; +uniform float lumaMin< + string label = "Luma min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma min smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.04; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; + +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} -} +float rand(vec2 n) +{ + return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); + //return sin(rand_f, n); +} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemLocked { +float noise(vec2 n) +{ + const vec2 d = vec2(0.0, 1.0); + vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); + return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); +} +float fbm(vec2 n) +{ + float total = 0.0, amplitude = 1.0; + for (int i = 0; i < 5; i++) + { + total += noise(n) * amplitude; + n += n * 1.7; + amplitude *= 0.47; + } + return total; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] -[Alias('obs.powershell.websocket.SetSceneItemLocked')] -param( +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale; + float flame_size = clamp(Flame_Size * .01,-5,5); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + // inverting direction is logically inverted to allow the bottom up to be normal + float fire_base = (v_in.uv.y / iResolution.y); + float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); + float direction = -1.0 * clamp(Speed*.01,-5,5); + if (!Invert) + { + direction *= -1.0; + fire_base = 1 - fire_base; + fire_pix = 1 - fire_pix; + } + float iTime = direction * elapsed_time; + + const vec3 c1 = vec3(0.5, 0.0, 0.1); + const vec3 c2 = vec3(0.9, 0.1, 0.0); + const vec3 c3 = vec3(0.2, 0.1, 0.7); + const vec3 c4 = vec3(1.0, 0.9, 0.1); + const vec3 c5 = vec3(0.1, 0.1, 0.1); + const vec3 c6 = vec3(0.9, 0.9, 0.9); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); + float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; + float alpha = saturate(Alpha_Percentage * .01); + + //change the constant term for all kinds of cool distance versions, + //make plus/minus to switch between + //ground fire and fire rain! + float dist = 3.5 - sin(iTime * 0.4) / 1.89; + + vec2 p = fire_pix * dist / iResolution.xx; + p.x -= iTime / 1.1; + float3 black = float3(0,0,0); + vec3 fire; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, + if (Fire_Type == 1) + { + //fire version 1 larger and more volume + float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); + float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); + float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; + float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; + q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemLocked')] -[switch] -$SceneItemLocked, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), + fbm(p - q - iTime* speed.y)) ; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); + fire += .05; + fire.r *= .8; + vec3 hsv = rgb2hsv(fire); + hsv.y *= hsv.z * 1.1; + hsv.z *= hsv.y * 1.13; + hsv.y = (2.2 - hsv.z * .9) * 1.20; + fire = hsv2rgb(hsv); + } + else + { + // fire version 0 - smaller and more whisps + p += (rand_f *.01); + float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); + float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); + float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; + float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; + q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; -process { + vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), + fbm(p + q - iTime * speed.y)) * shift; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); + + fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); + fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); + fire = fire/(1.0+max(black,fire)); + } + float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); + + // remove dark areas per user + float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire,0.0,alpha); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); + } + else + { + rgba = color; + } + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -15473,238 +20331,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemTransform { - +function Get-OBSFireworks2Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] -[Alias('obs.powershell.websocket.SetSceneItemTransform')] +[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Speed of OBSFireworks2Shader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemTransform')] -[PSObject] -$SceneItemTransform, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fireworks2' +$ShaderNoun = 'OBSFireworks2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// based on https://www.shadertoy.com/view/4dBGRw +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 1.0; +> = 100.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +#ifndef OPENGL +#define mat2 float2x2 +#define mix lerp +float mod(float x, float y) +{ + return x - y * floor(x / y); } +#endif +//Creates a diagonal red-and-white striped pattern. +float3 barberpole(float2 pos, float2 rocketpos){ + float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); + float3 col=float3(1.0,1.0,1.0); + d = mod(d*20.,2.0); + if(d>1.0){ + col=float3(1.0,0.0,0.0); + } + return col; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneName { - +float4 rocket(float2 pos, float2 rocketpos){ + float4 col = float4(0.0,0.0,0.0,0.0); + float f = 0.; + float absx= abs(rocketpos.x - pos.x); + float absy = abs(rocketpos.y-pos.y); + //wooden stick + if(absx<0.01&&absy<0.22){ + col=float4(1.0,0.5,0.5,1.0); + } + + //Barberpole + + if(absx<0.05&&absy<0.15){ + col=float4(barberpole(pos, rocketpos),1.0); + } + //Rocket Point + float pointw=(rocketpos.y-pos.y-0.25)*-0.7; + if((rocketpos.y-pos.y)>0.1){ + f=smoothstep(pointw-0.001,pointw+0.001,absx); + + col=mix(float4(1.0,0.0,0.0,1.0),col, f); + } + //Shadow + + f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); + col.rgb *= 0.7+f; + + return col; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] -[Alias('obs.powershell.websocket.SetSceneName')] -param( -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +float rand(float val, float seed){ + return cos(val*sin(val*seed)*seed); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newSceneName')] -[string] -$NewSceneName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } -process { +float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ + float4 col= float4(0.0,0.0,0.0,0.0); + float2 pp = float2(1.0,0.0); + mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); + for(float i=1.0;i<=128.0;i++){ + float d=rand(i, seed); + float fade=(i/128.0)*time; + float2 particpos = cpos + time*pp*d; + pp = mul(rr,pp); + col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); + } + col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); + col.a = col.r+col.g+col.b; + return col; +} +float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ + + float timeoffset = 2.0; + float4 col=float4(0.0,0.0,0.0,0.0); + if(time<=0.){ + return col; + } + time *= Speed /100.0; + if(mod(time, 6.0)>timeoffset){ + col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); + }else{ + + col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); + } + return col; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; + uv.y = -uv.y; + uv.x *= uv_size.x/uv_size.y; + float4 col = image.Sample(textureSampler, v_in.uv); + //col.rgb += 0.1*uv.y; + float4 c; + c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); + col = mix(col, c, c.a); + + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -15713,366 +20589,313 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneSceneTransitionOverride { - +function Get-OBSFireworksShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] +[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, - +# Set the show_flash of OBSFireworksShader +[Alias('show_flash')] +[ComponentModel.DefaultBindingProperty('show_flash')] +[Management.Automation.SwitchParameter] +$ShowFlash, +# Set the show_stars of OBSFireworksShader +[Alias('show_stars')] +[ComponentModel.DefaultBindingProperty('show_stars')] +[Management.Automation.SwitchParameter] +$ShowStars, +# Set the use_transparancy of OBSFireworksShader +[Alias('use_transparancy')] +[ComponentModel.DefaultBindingProperty('use_transparancy')] +[Management.Automation.SwitchParameter] +$UseTransparancy, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fireworks' +$ShaderNoun = 'OBSFireworksShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#ifndef OPENGL +#define mat2 float2x2 +#define fract frac +#define mix lerp +#endif +uniform bool show_flash = true; +uniform bool show_stars = true; +uniform bool use_transparancy = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float distLine(float2 p, float2 a, float2 b) { + float2 pa = p - a; + float2 ba = b - a; + float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * t); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float linef(float2 uv, float2 a, float2 b, float w) { + //return smoothstep(w, w - 0.01, distLine(uv, a, b)); + return w / distLine(uv, a, b); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float N21(float2 p) { + p = fract(p * float2(233.34, 851.73)); + p += dot(p, p + 23.45); + return fract(p.x * p.y); +} +float2 N22(float2 p) { + float n = N21(p); + return float2(n, N21(p + n)); } +float N11(float n) { + return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); +} -} +float particle(float2 uv, float2 p, float2 v, float r, float t) { + float g = -9.81; + float x = p.x + v.x * t; + float y = p.y + v.y * t + g / 2.0 * t * t; + float2 j = (float2(x, y) - uv) * 20.0; + float sparkle = 1.0 / dot(j, j); + return sparkle; +} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterEnabled { +float2 p1(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); +} +float2 p2(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] -[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] -param( +float endTime(float h) { + return pow(h, 1.0 / 5.0) * 1.1; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, +float explosion(float2 uv, float2 p, float s, float n, float f, float t) { -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, + float m = 0.0; + float dt = 0.5; + float seed2 = 0.32; + for(float i = 0.0; i < n; i++) { + seed2 += i; + float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); + float2 v = float2(cos(seed2), sin(seed2)) + rand; + m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); + } + return m; +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, +float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { + float2 p1v = p1(p, h, t); + float e = endTime(h); + return explosion(uv, p1v, s, n, f, t - e * 0.9); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterEnabled')] -[switch] -$FilterEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float shaft(float2 uv, float2 p, float w, float h, float t) { + float2 p1v = p1(p, h, t) + float2(0.0, 0.3); + float2 p2v = p2(p, h, t); + float e = 1.0 / 0.95 * endTime(h); + float2 j = (p1v - uv) * 15.0; + float sparkle = 1.0 / dot(j, j); + return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; +} +float3 base(float2 uv) { + return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); +} -process { +float back(float2 uv, float2 p, float t) { + float dt = 0.3; + float j = length(p - uv); + float m = exp(-0.005 * j * j); + return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); +} +float stars(float2 uv) { + float r = N21(uv); + return smoothstep(0.001, 0.0, r); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float mod(float x, float y) +{ + return x - y * floor(x / y); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv - float2(0.5,0.5); + uv.y = uv.y * -1; + float t = elapsed_time / 10.0; + float scale = 10.0; + uv *= scale; + // + float4 col = image.Sample(textureSampler, v_in.uv); + if(show_stars){ + float c = stars(uv); + if(use_transparancy){ + col += float4(c,c,c,c)*(1.0-col.a); + }else{ + col += float4(c,c,c,c);//*(1.0-orig_col.a); } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + + } + + float a = -0.035 * sin(t * 15.0); + float co = cos(a); + float si = sin(a); + mat2 trans1 = mat2(float2(co, si), float2(-si, co)); + float2 trans2 = float2(-15.0 * a, 0.0); +#ifndef OPENGL + uv = mul(uv, trans1); +#else + uv *= trans1; +#endif + uv += trans2; + + for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { + float ti = mod(t * 9.0 - i * 5.0, 4.0); + float scale = mix(2.0, 0.3, ti / 4.0); + float2 uvs = uv * scale; + float rand = N11(i); + float h = 10.0 + rand * 4.0; + float w = 0.02; + float n = 80.0; + float s = 0.9; + float f = 1.5; + float2 p = float2(mix(-8.0, 8.0, rand), -10.0); + float fw = fireworks(uvs, p, h, n, s, f, ti); + float3 bc = base(uv); + col += float4(bc*fw, fw); + col += shaft(uvs, p, w, h, ti); + if(show_flash){ + if(use_transparancy){ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; + }else{ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8); } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + } + + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterIndex { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] -[Alias('obs.powershell.websocket.SetSourceFilterIndex')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$FilterIndex, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16081,120 +20904,188 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterName { - +function Get-OBSFisheyeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] -[Alias('obs.powershell.websocket.SetSourceFilterName')] +[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] param( - +# Set the center_x_percent of OBSFisheyeShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Single] +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Single] +$CenterYPercent, +# Set the power of OBSFisheyeShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newFilterName')] -[string] -$NewFilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fisheye' +$ShaderNoun = 'OBSFisheyeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float center_x_percent< + string label = "Center x percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power< + string label = "Power"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); + } else if(power <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; } + uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); + } + return image.Sample(textureSampler, uv); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16203,125 +21094,213 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterSettings { - +function Get-OBSFisheyeXyShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] -[Alias('obs.powershell.websocket.SetSourceFilterSettings')] +[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] param( - +# Set the center_x_percent of OBSFisheyeXyShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Single] +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeXyShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Single] +$CenterYPercent, +# Set the power_x of OBSFisheyeXyShader +[Alias('power_x')] +[ComponentModel.DefaultBindingProperty('power_x')] +[Single] +$PowerX, +# Set the power_y of OBSFisheyeXyShader +[Alias('power_y')] +[ComponentModel.DefaultBindingProperty('power_y')] +[Single] +$PowerY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'fisheye-xy' +$ShaderNoun = 'OBSFisheyeXyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float center_x_percent< + string label = "Center x percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power_x< + string label = "Power x"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; +uniform float power_y< + string label = "Power y"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power_x >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; + } else if(power_x <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; + } + if (power_y >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; + } else if(power_y <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; + } + return image.Sample(textureSampler, uv); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16330,110 +21309,163 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStreamServiceSettings { - +function Get-OBSFlipShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] -[Alias('obs.powershell.websocket.SetStreamServiceSettings')] +[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceType')] -[string] -$StreamServiceType, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceSettings')] -[PSObject] -$StreamServiceSettings, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Horizontal of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the Vertical of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Flip' +$ShaderNoun = 'OBSFlipShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// A Simple Flip Shader +uniform bool Horizontal< + string label = "Flip horizontally"; +> = true; +uniform bool Vertical< + string label = "Flip vertically"; +> = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + if (Horizontal == true) { + pos.x = 1 - pos.x; + } + if (Vertical == true) { + pos.y = 1 - pos.y; + } + + return image.Sample(textureSampler, pos); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16442,105 +21474,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStudioModeEnabled { - +function Get-OBSFrostedGlassShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] -[Alias('obs.powershell.websocket.SetStudioModeEnabled')] +[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('studioModeEnabled')] -[switch] -$StudioModeEnabled, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Alpha_Percent of OBSFrostedGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Amount of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Amount')] +[Single] +$Amount, +# Set the Scale of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Scale')] +[Single] +$Scale, +# Set the Animate of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the Horizontal_Border of OBSFrostedGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSFrostedGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSFrostedGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the notes of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'frosted_glass' +$ShaderNoun = 'OBSFrostedGlassShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter + +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Amount< + string label = "Amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.03; +uniform float Scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 5.1; +uniform bool Animate; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.1; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform string notes< + string widget_type = "info"; +> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; +float rand(float2 co) +{ + float scale = Scale; + if (Animate) + scale *= rand_f; + float2 v1 = float2(92.0,80.0); + float2 v2 = float2(41.0,62.0); + return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + float3 tc = rgba.rgb * Border_Color.rgb; + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + if (uv_compare < (Border_Offset - 0.005)) + { + float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); + tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + tc = image.Sample(textureSampler, v_in.uv).rgb; + } + return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16549,111 +21714,184 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSTBarPosition { - +function Get-OBSGammaCorrectionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] -[Alias('obs.powershell.websocket.SetTBarPosition')] +[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('position')] -[ValidateRange(0,1)] -[double] -$Position, - +# Set the Red of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Red')] +[Single] +$Red, +# Set the Green of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Green')] +[Single] +$Green, +# Set the Blue of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the notes of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('release')] -[switch] -$Release, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +process { +$shaderName = 'gamma_correction' +$ShaderNoun = 'OBSGammaCorrectionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float Red< + string label = "Red"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Green< + string label = "Green"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Blue< + string label = "Blue"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform string notes< + string widget_type = "info"; +> = "Modify Colors to correct for gamma, use equal values for general correction." - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); + float4 c = image.Sample(textureSampler, v_in.uv); + c.rgb = pow(c.rgb, 1.0 / gammaRGB); + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16662,243 +21900,264 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSVideoSettings { - +function Get-OBSGaussianBlurAdvancedShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] -[Alias('obs.powershell.websocket.SetVideoSettings')] +[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsNumerator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsNumerator, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsDenominator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsDenominator, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseWidth')] -[ValidateRange(1,4096)] -[double] -$BaseWidth, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseHeight')] -[ValidateRange(1,4096)] -[double] -$BaseHeight, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputWidth')] -[ValidateRange(1,4096)] -[double] -$OutputWidth, - +# Set the Directions of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Directions')] +[Single] +$Directions, +# Set the Quality of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Quality')] +[Single] +$Quality, +# Set the Size of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Size')] +[Single] +$Size, +# Set the Mask_Left of OBSGaussianBlurAdvancedShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurAdvancedShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurAdvancedShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputHeight')] -[ValidateRange(1,4096)] -[double] -$OutputHeight, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gaussian-blur-advanced' +$ShaderNoun = 'OBSGaussianBlurAdvancedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Directions< + string label = "Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) +uniform float Quality< + string label = "Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float Size< + string label = "Size (8.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 8.0; // BLUR SIZE (Radius) +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 + + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSOutput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] -[Alias('obs.powershell.websocket.StartOutput')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -16907,100 +22166,342 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Start-OBSRecord { - +function Get-OBSGaussianBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] -[Alias('obs.powershell.websocket.StartRecord')] +[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the imageSize of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageSize')] +[Single[]] +$ImageSize, +# Set the imageTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageTexel')] +[Single[]] +$ImageTexel, +# Set the u_radius of OBSGaussianBlurShader +[Alias('u_radius')] +[ComponentModel.DefaultBindingProperty('u_radius')] +[Int32] +$URadius, +# Set the u_diameter of OBSGaussianBlurShader +[Alias('u_diameter')] +[ComponentModel.DefaultBindingProperty('u_diameter')] +[Int32] +$UDiameter, +# Set the u_texelDelta of OBSGaussianBlurShader +[Alias('u_texelDelta')] +[ComponentModel.DefaultBindingProperty('u_texelDelta')] +[Single[]] +$UTexelDelta, +# Set the elapsed_time of OBSGaussianBlurShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianBlurShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianBlurShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianBlurShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the kernel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernel')] +[String] +$Kernel, +# Set the kernelTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernelTexel')] +[Single[]] +$KernelTexel, +# Set the pixel_size of OBSGaussianBlurShader +[Alias('pixel_size')] +[ComponentModel.DefaultBindingProperty('pixel_size')] +[Single] +$PixelSize, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gaussian-blur' +$ShaderNoun = 'OBSGaussianBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +// OBS Default +uniform float4x4 ViewProj; +// Settings (Shared) +uniform texture2d image; +uniform float2 imageSize; +uniform float2 imageTexel; +uniform int u_radius; +uniform int u_diameter; +uniform float2 u_texelDelta; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +// Settings (Private) +//uniform float registerkernel[25]; +uniform texture2d kernel; +uniform float2 kernelTexel; +uniform float pixel_size = 1.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +sampler_state pointClampSampler { + Filter = Point; + AddressU = Clamp; + AddressV = Clamp; +}; + +sampler_state bilinearClampSampler { + Filter = Bilinear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float Gaussian(float x, float o) +{ + float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); +} + +VertData VSDefault(VertData vert_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv; + return vert_out; +} + +float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel) + { + float l_gauss = Gaussian(0.0, 1.0); + float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_gauss += l_g; + } + l_value = l_value * (1.0 / l_gauss); + return l_value; +} + +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + } + return l_value; +} + +/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + if (pixel_size % 2 == 0) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = p_texel; + float2 l_koffset = p_kernelTexel; + for (int k = 1; k <= pixel_size; k++) { + float l_g = kernel.Sample(pointClampSampler, l_koffset).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_uvoffset += p_texel; + l_koffset += p_kernelTexel; + } + return l_value; + } else { + return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) + } +}*/ + +float4 PSGaussian(VertData vert_in) : TARGET +{ + + float4 color = image.Sample(pointClampSampler, vert_in.uv); + + float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + + return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); + + /* + return InternalGaussianPrecalculated( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ + + /* + return InternalGaussianPrecalculatedNVOptimize( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSGaussian(vert_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -17009,202 +22510,249 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Start-OBSReplayBuffer { - +function Get-OBSGaussianBlurSimpleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] -[Alias('obs.powershell.websocket.StartReplayBuffer')] +[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +# Set the Strength of OBSGaussianBlurSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Int32] +$Strength, +# Set the Mask_Left of OBSGaussianBlurSimpleShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurSimpleShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurSimpleShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurSimpleShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gaussian-blur-simple' +$ShaderNoun = 'OBSGaussianBlurSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int Strength< + string label = "Strength (1)"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 1.0; +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + if(Strength <= 0) + return image.Sample(textureSampler, v_in.uv); + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 + + float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) + float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) + + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSStream { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] -[Alias('obs.powershell.websocket.StartStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -17213,310 +22761,378 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Start-OBSVirtualCam { - +function Get-OBSGaussianExampleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] -[Alias('obs.powershell.websocket.StartVirtualCam')] +[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianExampleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianExampleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianExampleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSGaussianExampleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSGaussianExampleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the initial_image of OBSGaussianExampleShader +[Alias('initial_image')] +[ComponentModel.DefaultBindingProperty('initial_image')] +[String] +$InitialImage, +# Set the before_image of OBSGaussianExampleShader +[Alias('before_image')] +[ComponentModel.DefaultBindingProperty('before_image')] +[String] +$BeforeImage, +# Set the after_image of OBSGaussianExampleShader +[Alias('after_image')] +[ComponentModel.DefaultBindingProperty('after_image')] +[String] +$AfterImage, +# Set the text_color of OBSGaussianExampleShader +[Alias('text_color')] +[ComponentModel.DefaultBindingProperty('text_color')] +[String] +$TextColor, +# Set the max_distance of OBSGaussianExampleShader +[Alias('max_distance')] +[ComponentModel.DefaultBindingProperty('max_distance')] +[Single] +$MaxDistance, +# Set the exp of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('exp')] +[Single] +$Exp, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gaussian-example' +$ShaderNoun = 'OBSGaussianExampleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +/*-------------------------. +| :: Texture and sampler:: | +''-------------------------*/ - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform texture2d initial_image; +sampler_state initial_sampler +{ + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = initial_image; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform texture2d before_image; +sampler_state before_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = before_image; +}; -} +uniform texture2d after_image; +sampler_state after_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = after_image; +}; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -} +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSOutput { +struct ColorData { + float4 initial_color : SV_TARGET0; + float4 before_color: SV_TARGET1; + float4 after_color : SV_TARGET2; +}; +uniform float4 text_color; +uniform float max_distance; +uniform float exp; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] -[Alias('obs.powershell.websocket.StopOutput')] -param( +#define PI 3.141592653589793238462643383279502884197169399375105820974 -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +VertData mainTransform(VertData v_in) +{ + VertData vert_out = v_in; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} +float4 grayscale(float4 color) +{ + float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; + return float4(grayscale, grayscale, grayscale, color.a); +} -process { +float4 gaussian(VertData v_in, float angle) +{ + float rad = radians(angle); + float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); + float2 dir_2 = dir * 2.0; + float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; + + float4 px_away = image.Sample(textureSampler, v_in.uv + dir); + px_away += image.Sample(textureSampler, v_in.uv - dir); + px_away *= 0.25; + + float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away *= 0.0625; + + return ret + px_away + px_2_away; +} +ColorData setColorData(VertData v_in): SV_TARGET0 +{ + //string RenderTarget0 = "initial_image"; + ColorData cd;// = (ColorData)0; + cd.initial_color = image.Sample(textureSampler, v_in.uv); + cd.before_color = float4(0.0,0.0,1.0,1.0); + cd.after_color = float4(1.0,0.0,0.0,1.0); + return cd; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 blurImageH(VertData v_in) : SV_TARGET1 +{ + //string RenderTarget1 = "before_image"; + //ColorData cd = (ColorData)0; + //cd.initial_color = image.Sample(textureSampler, v_in.uv); + //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); + return float4(0.0,0.0,1.0,1.0); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 blurImageV(VertData v_in) : SV_TARGET2 +{ + //string RenderTarget2 = "after_image"; + //ColorData cd = (ColorData)0; + //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); + return float4(1.0,0.0,0.0,1.0); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : SV_TARGET0 +{ + float4 color; + ColorData cd;// = (ColorData)0; + + //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); + //cd.before_color = before_image.Sample(before_sampler, v_in.uv); + cd.after_color = after_image.Sample(before_sampler, v_in.uv); + + if (max_distance <= 5) { + color = cd.before_color; + } + else { + color = cd.after_color;//image.Sample(textureSampler, v_in.uv); + } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + float4 gray = grayscale(color); + float4 gray_text = grayscale(text_color); + float d = distance(gray.rgb, gray_text.rgb); + if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ + float d_c = pow(d*2, exp) / pow(2, exp); + d_c = sin(d_c * PI / 2); + d = pow(1.0 - sin(d * PI/4), exp); - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + color.rgb = float3(d,d,d); + } + return color; } +technique Draw +{ + pass pre + { + vertex_shader = mainTransform(v_in); + pixel_shader = setColorData(v_in); + } -} - - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] -[Alias('obs.powershell.websocket.StopRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + pass b0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageH(v_in); + } + pass b1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageV(v_in); + } -process { + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -17525,304 +23141,290 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Stop-OBSReplayBuffer { - +function Get-OBSGaussianSimpleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] -[Alias('obs.powershell.websocket.StopReplayBuffer')] +[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSStream { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] -[Alias('obs.powershell.websocket.StopStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianSimpleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianSimpleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianSimpleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianSimpleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSGaussianSimpleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSGaussianSimpleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSGaussianSimpleShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSGaussianSimpleShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSGaussianSimpleShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the samples of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the LOD of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('LOD')] +[Int32] +$LOD, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gaussian-simple' +$ShaderNoun = 'OBSGaussianSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/ltScRG Converted inspiration +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float4x4 ViewProj; +uniform texture2d image; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +// 16x acceleration of https://www.shadertoy.com/view/4tSyzy +// by applying gaussian at intermediate MIPmap level. - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform int samples< + string label = "Samples"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 25; + int step = 1; +> = 16; +uniform int LOD< + string label = "LOD"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 2; // gaussian done on MIPmap at scale LOD +float gaussian(vec2 i) +{ + float sigma = (float(samples) * .25); + return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); } +vec4 blur(vec2 U, vec2 scale) +{ + vec4 O = vec4(0,0,0,0); + int sLOD = (1 << LOD); // tile size = 2^LOD + int s = samples / sLOD; + + for (int i = 0; i < s * s; i++) + { + vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; + O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); + //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); + //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); + } + + return O / O.a; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSVirtualCam { +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; + //float2 iResolution = 1 - v_in.uv + 1.0; + //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); + return blur(v_in.uv / iResolution, 1.0 / iResolution); + //return rgba; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] -[Alias('obs.powershell.websocket.StopVirtualCam')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) -process { - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -17831,219 +23433,335 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSInputMute { - +function Get-OBSGbCameraShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] -[Alias('obs.powershell.websocket.ToggleInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] param( - +# Set the pixelSize of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('pixelSize')] +[Single] +$PixelSize, +# Set the dither_factor of OBSGbCameraShader +[Alias('dither_factor')] +[ComponentModel.DefaultBindingProperty('dither_factor')] +[Single] +$DitherFactor, +# Set the alternative_bayer of OBSGbCameraShader +[Alias('alternative_bayer')] +[ComponentModel.DefaultBindingProperty('alternative_bayer')] +[Management.Automation.SwitchParameter] +$AlternativeBayer, +# Set the brightness of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the contrast of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('contrast')] +[Single] +$Contrast, +# Set the gamma of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('gamma')] +[Single] +$Gamma, +# Set the color_1 of OBSGbCameraShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the color_2 of OBSGbCameraShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the color_3 of OBSGbCameraShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the color_4 of OBSGbCameraShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'gb-camera' +$ShaderNoun = 'OBSGbCameraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +/* + * ------------------------------------------------------------ + * "THE BEERWARE LICENSE" (Revision 42): + * maple wrote this code. As long as you retain this + * notice, you can do whatever you want with this stuff. If we + * meet someday, and you think this stuff is worth it, you can + * buy me a beer in return. + * ------------------------------------------------------------ + * from https://www.shadertoy.com/view/3tSXRh + * adopted for OBS by Exeldro + * ------------------------------------------------------------ + */ +uniform float pixelSize< + string label = "Pixel Size"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 50.0; + float step = 0.1; +> = 3.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float dither_factor< + string label = "Dither Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform bool alternative_bayer; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float brightness< + string label = "Brightness"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float contrast< + string label = "Contrast"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float gamma< + string label = "Gamma"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.6; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; +uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; +uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; +uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; +// quantize coords to low resolution +float2 pixelize(float2 uv, float2 pixelSize) { + float2 factor = pixelSize / uv_size; + return floor(uv / factor) * factor; } +float3 colorLUT(float3 color) { + float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; + if(gray < 0.25) + return color_1.rgb; + if(gray < 0.50) + return color_2.rgb; + if(gray < 0.75) + return color_3.rgb; + return color_4.rgb; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSOutput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] -[Alias('obs.powershell.websocket.ToggleOutput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// adjust brightness, contrast and gamma levels of a color +float3 levels(float3 color, float brightness, float contrast, float3 gamma) { + float3 value = (color - 0.5) * contrast + 0.5; + value = clamp(value + brightness, 0.0, 1.0); + return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); +} +float3 levels(float3 color, float brightness, float contrast, float gamma) { + return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); +} +// applies the dithering filter to a color map +float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { + // reduces pixel space to the selected pixel size + float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); + + // applies the bayer matrix filter to the color map + pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); + int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); + float bayer; + if (alternative_bayer){ +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ + 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ + 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ + 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ + 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; + } else { +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 48, 12, 60, 3, 51, 15, 63, + 32, 16, 44, 28, 35, 19, 47, 31, + 8, 56, 4, 52, 11, 59, 7, 55, + 40, 24, 36, 20, 43, 27, 39, 23, + 2, 50, 14, 62, 1, 49, 13, 61, + 34, 18, 46, 30, 33, 17, 45, 29, + 10, 58, 6, 54, 9, 57, 5, 53, + 42, 26, 38, 22, 41, 25, 37, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; + } + float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); + // limits it to the selected palette + color = colorLUT(bayerColor); -process { + return color; +} +float4 mainImage(VertData v_in) : TARGET +{ + float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); + texcoord = clamp(texcoord, 0.001, 1.0); + float4 c = image.Sample(textureSampler, texcoord); + float3 color = c.rgb; + + color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); + + color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); + + return float4(color.r, color.g, color.b, c.a); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -18052,306 +23770,267 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecord { - +function Get-OBSGlassShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] -[Alias('obs.powershell.websocket.ToggleRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Alpha_Percent of OBSGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Offset_Amount of OBSGlassShader +[Alias('Offset_Amount')] +[ComponentModel.DefaultBindingProperty('Offset_Amount')] +[Single] +$OffsetAmount, +# Set the xSize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('xSize')] +[Int32] +$XSize, +# Set the ySize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('ySize')] +[Int32] +$YSize, +# Set the Reflection_Offset of OBSGlassShader +[Alias('Reflection_Offset')] +[ComponentModel.DefaultBindingProperty('Reflection_Offset')] +[Int32] +$ReflectionOffset, +# Set the Horizontal_Border of OBSGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the Glass_Color of OBSGlassShader +[Alias('Glass_Color')] +[ComponentModel.DefaultBindingProperty('Glass_Color')] +[String] +$GlassColor, +# Set the notes of OBSGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'glass' +$ShaderNoun = 'OBSGlassShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Offset_Amount< + string label = "Offset Amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; +uniform int xSize< + string label = "x Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int ySize< + string label = "y Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int Reflection_Offset< + string label = "Reflection Offset"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 2; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = -0.01; + float maximum = 1.01; + float step = 0.01; +> = 0.5; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform float4 Glass_Color; +uniform string notes< + string widget_type = "info"; +> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float mod(float a, float b){ + float d = a / b; + return (d-floor(d))*b; } +float4 mainImage(VertData v_in) : TARGET +{ + -} - - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecordPause { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] -[Alias('obs.powershell.websocket.ToggleRecordPause')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - + int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); + int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); + float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); + float2 uv = v_in.uv + offsets; + float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 rgba = image.Sample(textureSampler, v_in.uv); + float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); + rgba = image.Sample(textureSampler, uv); + float4 rgba_glass = image.Sample(textureSampler, uv2); + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + if (uv_compare < (Border_Offset - 0.005)) + { + rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + rgba_output = image.Sample(textureSampler, v_in.uv); + } + return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] -[Alias('obs.powershell.websocket.ToggleReplayBuffer')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -18360,204 +24039,352 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSStream { - +function Get-OBSGlitchAnalogShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] -[Alias('obs.powershell.websocket.ToggleStream')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader +[Alias('scan_line_jitter_displacement')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] +[Single] +$ScanLineJitterDisplacement, +# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader +[Alias('scan_line_jitter_threshold_percent')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] +[Int32] +$ScanLineJitterThresholdPercent, +# Set the vertical_jump_amount of OBSGlitchAnalogShader +[Alias('vertical_jump_amount')] +[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] +[Single] +$VerticalJumpAmount, +# Set the vertical_speed of OBSGlitchAnalogShader +[Alias('vertical_speed')] +[ComponentModel.DefaultBindingProperty('vertical_speed')] +[Single] +$VerticalSpeed, +# Set the horizontal_shake of OBSGlitchAnalogShader +[Alias('horizontal_shake')] +[ComponentModel.DefaultBindingProperty('horizontal_shake')] +[Single] +$HorizontalShake, +# Set the color_drift_amount of OBSGlitchAnalogShader +[Alias('color_drift_amount')] +[ComponentModel.DefaultBindingProperty('color_drift_amount')] +[Single] +$ColorDriftAmount, +# Set the color_drift_speed of OBSGlitchAnalogShader +[Alias('color_drift_speed')] +[ComponentModel.DefaultBindingProperty('color_drift_speed')] +[Single] +$ColorDriftSpeed, +# Set the pulse_speed_percent of OBSGlitchAnalogShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the alpha_percent of OBSGlitchAnalogShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the rotate_colors of OBSGlitchAnalogShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Replace_Image_Color of OBSGlitchAnalogShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGlitchAnalogShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSGlitchAnalogShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'glitch_analog' +$ShaderNoun = 'OBSGlitchAnalogShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float scan_line_jitter_displacement< + string label = "Scan line jitter"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.0; // (displacement, threshold) +uniform int scan_line_jitter_threshold_percent< + string label = "scan line jitter threshold percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 95; +uniform float vertical_jump_amount< + string label = "Vertical jump amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float vertical_speed< + string label = "Vertical speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform float horizontal_shake< + string label = "Horizontal shake"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_amount< + string label = "Color drift amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_speed< + string label = "Color drift speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int alpha_percent< + string label = "Aplha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> ="play with settings!"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float nrand(float x, float y) +{ + float value = dot(float2(x, y), float2(12.9898 , 78.233 )); + return frac(sin(value) * 43758.5453); } +float4 mainImage(VertData v_in) : TARGET +{ + float speed = pulse_speed_percent * 0.01; + float alpha = alpha_percent * 0.01; + float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; + float u = v_in.uv.x; + float v = v_in.uv.y; + float t = sin(elapsed_time * speed) * 2 - 1; + float4 rgba = image.Sample(textureSampler, v_in.uv); -} + // Scan line jitter + float jitter = nrand(v, t) * 2 - 1; + jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSVirtualCam { + // Vertical jump + float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); + // Horizontal shake + float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] -[Alias('obs.powershell.websocket.ToggleVirtualCam')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //// Color drift + float drift = sin(jump + color_drift_speed) * color_drift_amount; + float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); + float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); + + if(rotate_colors) + { + // get general time number between 0 and 4 + float tx = (t + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls + //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 + src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); + //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 + src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); + //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 + src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); + + } -process { + float4 color = rgba; + float4 original_color = color; + rgba = float4(src1.x, src2.x, src1.y, alpha); + if (Apply_To_Alpha_Layer) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color, alpha); + } + + if (Apply_To_Specific_Color) + { + color = original_color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -18566,70 +24393,26 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBS3dPanelShader { +function Get-OBSGlitchPeriodicShader { -[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] +[Alias('Set-OBSGlitchPeriodicShader','Add-OBSGlitchPeriodicShader')] param( -# Set the credits of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('credits')] -[String] -$Credits, -# Set the scale of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, -# Set the tilt_x_deg of OBS3dPanelShader -[Alias('tilt_x_deg')] -[ComponentModel.DefaultBindingProperty('tilt_x_deg')] -[Single] -$TiltXDeg, -# Set the tilt_y_deg of OBS3dPanelShader -[Alias('tilt_y_deg')] -[ComponentModel.DefaultBindingProperty('tilt_y_deg')] -[Single] -$TiltYDeg, -# Set the tilt_z_deg of OBS3dPanelShader -[Alias('tilt_z_deg')] -[ComponentModel.DefaultBindingProperty('tilt_z_deg')] -[Single] -$TiltZDeg, -# Set the pos_x of OBS3dPanelShader -[Alias('pos_x')] -[ComponentModel.DefaultBindingProperty('pos_x')] -[Single] -$PosX, -# Set the pos_y of OBS3dPanelShader -[Alias('pos_y')] -[ComponentModel.DefaultBindingProperty('pos_y')] +# Set the PERI of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('PERI')] [Single] -$PosY, -# Set the thickness of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('thickness')] -[Single] -$Thickness, -# Set the radius_fb of OBS3dPanelShader -[Alias('radius_fb')] -[ComponentModel.DefaultBindingProperty('radius_fb')] +$PERI, +# Set the DURA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('DURA')] [Single] -$RadiusFb, -# Set the brightness of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('brightness')] +$DURA, +# Set the AMPL of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('AMPL')] [Single] -$Brightness, -# Set the light_position of OBS3dPanelShader -[Alias('light_position')] -[ComponentModel.DefaultBindingProperty('light_position')] -[Int32] -$LightPosition, -# Set the wiggle of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('wiggle')] +$AMPL, +# Set the SCRA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('SCRA')] [Single] -$Wiggle, -# Set the wiggle_rot of OBS3dPanelShader -[Alias('wiggle_rot')] -[ComponentModel.DefaultBindingProperty('wiggle_rot')] -[Management.Automation.SwitchParameter] -$WiggleRot, +$SCRA, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -18660,265 +24443,102 @@ $UseShaderTime process { -$shaderName = '3d-panel' -$ShaderNoun = 'OBS3dPanelShader' +$shaderName = 'glitch-periodic' +$ShaderNoun = 'OBSGlitchPeriodicShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://x.com/HoraiChan/status/1986268258883010766 +// Created by Éric Nicolas (ccjmne) for use with obs-shaderfilter 12/2025 +// Port of: https://www.shadertoy.com/view/WfVfDh +// Originally forked from: https://www.shadertoy.com/view/MtXBDs -uniform string credits< - string widget_type = "info"; -> = "Based on effect by Horaiken"; +#define PI 3.14159265359 -uniform float scale< - string label = "大きさ / Scale"; - string widget_type = "slider"; - float minimum = 0.25; - float maximum = 3.00; - float step = 0.001; -> = 1.0; -uniform float tilt_x_deg< - string label = "縦方向の傾き(X) / Tilt (X)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 20.0; -uniform float tilt_y_deg< - string label = "横方向の傾き(Y) / Tilt (Y)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 35.0; -uniform float tilt_z_deg< - string label = "回転(Z) / Roll (Z)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 0.0; -uniform float pos_x< - string label = "横位置 / Horizontal Position"; - string widget_type = "slider"; - float minimum = -1.00; - float maximum = 1.00; - float step = 0.0001; -> = 0.0; -uniform float pos_y< - string label = "縦位置 / Vertical Position"; - string widget_type = "slider"; - float minimum = -1.00; - float maximum = 1.00; - float step = 0.0001; -> = 0.0; -uniform float thickness< - string label = "厚み / Thickness"; +/* For visual explanation of the paramters, see */ +/* https://www.desmos.com/calculator/vezu1wyqma */ +/* */ +/* Period How often a glitch occurs (in seconds) 0–? */ +/* Duration How long a glitch lasts (in seconds) 0–Period */ +/* Amplitude How intense a glitch is 0–1 */ +/* Scratchiness How jittery a glitch is 0–1 */ + +uniform float PERI< + string label = "Period"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 0.1; - float step = 0.001; -> = 0.03; -uniform float radius_fb< - string label = "角丸 / Corner Radius"; + float minimum = 1.; + float maximum = 60.; + float step = 1.; +> = 6.; + +uniform float DURA< + string label = "Duration"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.2; -uniform float brightness< - string label = "明るさ / Brightness"; + float minimum = 0.; + float maximum = 60.; + float step = .01; +> = .5; + +uniform float AMPL< + string label = "Amplitude"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 2.00; - float step = 0.01; -> = 1.2; -uniform int light_position < - string label = "照明の位置 / Light Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "左側に光 / Light From Left"; - int option_1_value = 1; - string option_1_label = "右側に光 / Light From Right"; -> = 0; -uniform float wiggle < - string label = "ゆらゆら / Wiggle"; + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .15; + +uniform float SCRA< + string label = "Scratchiness"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 2.50; - float step = 0.01; -> = 0.0; -uniform bool wiggle_rot < - string label = "角度もゆらゆら / Wiggle Rotation"; ->; - -float hash1(float n){ return frac(sin(n)*43758.5453123); } - -float noise1D(float x) { - float i = floor(x); - float f = frac(x); - float u = f*f*(3.0 - 2.0*f); - return lerp(hash1(i), hash1(i+1.0), u); // 0..1 -} - -float fbm1D(float x) { - float v = 0.0; - float a = 0.5; - float f = 1.0; - for(int k=0;k<4;k++){ - v += a * noise1D(x * f); - f *= 2.0; - a *= 0.5; - } - return v; -} - -float saturate(float x) { return clamp(x, 0.0, 1.0); } - -float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } -float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } -float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } - -// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) -float sdRoundRect2D(float2 p, float2 bxy, float r) { - float2 q = abs(p) - bxy + r; - return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; -} + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .2; -// 正面シルエット角丸 + Z方向に押し出し -float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { - float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); - float a = sdRoundRect2D(p.xy, b.xy, r_fb); - float dz = abs(p.z) - b.z; - return max(a, dz); +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); } -// 法線 -float3 calcNormal(float3 p, float3 b, float rfb) { - const float e = 0.001; - float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); - float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); - float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); - float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); - return normalize(float3(dx,dy,dz)); +float randomRange(in float2 seed, in float lo, in float hi) { + return lo + random2d(seed) * (hi - lo); } -// 照明 -float3 shade(float3 n, float3 v) { - float3 l; - if (light_position == 0) { // 左から光 - l = normalize(float3(-1.0, -0.1, 1.0)); - } - else { // 右から光 - l = normalize(float3( 1.0, -0.1, 1.0)); - } - float diff = saturate(dot(n,l)); - float rim = pow(1.0 - saturate(dot(n,v)), 2.0); - float li = 0.25 + 0.75*diff + 0.08*rim; - return float3(li, li, li); +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); } -float4 mainImage(VertData v_in) : TARGET { +float4 mainImage(VertData v_in): TARGET { + float time = floor(elapsed_time * SCRA * 60.); float2 uv = v_in.uv; - // 画面座標(短辺基準) - float aspect = uv_size.x / uv_size.y; - float2 ndc = uv * 2.0 - 1.0; - ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); - float2 p2 = ndc; - p2.x *= aspect; - - // カメラ設定 - float3 ro = float3(0.0, 0.0, 3.2); - float3 rd = normalize(float3(p2, -4.0)); - - // 回転(Z→Y→X の順に逆回転) - float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); - ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); - rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); - - // 画面フィット(短辺基準)+ 厚み - float2 baseXY; - if (aspect > 1.0) { - baseXY = float2(1.0, 1.0 / aspect); - } else { - const float portraitMargin = 0.6; - baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); - } - float3 b = float3(baseXY, thickness) * max(scale, 0.0001); - - // Wiggle - float diag = length(2.0 * b); - float amp = 0.05 * wiggle * diag; - const float WSPD = 0.1; - - // 各軸に独立ノイズ - float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; - float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; - float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; - float3 woff = float3(wx, wy, wz); + // Periodic intermittence + float AMP = AMPL * (cos(2. * PI * max(0., (mod(-elapsed_time / PERI, 1.) - 1.) * PERI / DURA + 1.)) * -.5 + .5); - float rotAmp = radians(12.0) * wiggle; - - float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; - float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; - - float3 ro2 = ro; - float3 rd2 = rd; - - if (wiggle_rot) { - ro2 = rotateX(ro2, wobX); - ro2 = rotateY(ro2, wobY); - rd2 = rotateX(rd2, wobX); - rd2 = rotateY(rd2, wobY); - } + float4 outCol = image.Sample(textureSampler, uv); - float t = 0.0; - float d = 0.0; - bool hit = false; - for (int i=0; i<64; i++) { - float3 pos = ro2 + rd2 * t; - d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); - if (d < 0.001) { hit = true; break; } - t += d; - if (t > 8.0) break; + // Randomly offset slices horizontally + float offsetMax = AMP / 2.; + for (float i = 0.; i < 10. * AMP; i += 1.) { + float sliceY = random2d( float2(time, 2345. + i)); + float sliceH = random2d( float2(time, 9035. + i)) * .25; + float offsetH = randomRange(float2(time, 9625. + i), -offsetMax, offsetMax); + float2 uvOff = uv; + uvOff.x += offsetH; + if (insideRange(uv.y, sliceY, frac(sliceY + sliceH)) == 1.) { + outCol = image.Sample(textureSampler, uvOff); + } } - // ヒットしなければ完全透明(元ソースは非表示) - if (!hit) return float4(0.0, 0.0, 0.0, 0.0); - - float3 pos = ro2 + rd2 * t; - float3 pObj = pos - woff; - float3 n = calcNormal(pObj, b, radius_fb); - float3 vdir = normalize(-rd2); - - // テクスチャ貼り付け - float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); - float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; - - // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) - float4 texFront = image.Sample(textureSampler, uvTex); - float4 texEdge = image.Sample(textureSampler, uvTex); - float4 tex = lerp(texEdge, texFront, frontMask); - - // フロント面エッジ・ハイライト(細い線) - float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); - float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF - float edgeWidth = 0.02 * min(b.x, b.y); - float edgeIntensity = 0.6; - float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); - float edgeMask = frontMask * edgeProx; - tex.rgb *= (1.0 + edgeMask * edgeIntensity); - - // 照明 - float3 lightTerm = shade(n, vdir); - tex.rgb *= lightTerm; - - // 明るさスライダ - tex.rgb *= brightness; + // Slightly offset one entire channel + offsetMax = AMP / 6.; + float2 colOff = float2( + randomRange(float2(time, 9545.), -offsetMax, offsetMax), + randomRange(float2(time, 7205.), -offsetMax, offsetMax) + ); + float rnd = random2d(float2(time , 9545.)); + if (rnd < .33) outCol.r = image.Sample(textureSampler, uv + colOff).r; + else if (rnd < .66) outCol.g = image.Sample(textureSampler, uv + colOff).g; + else outCol.b = image.Sample(textureSampler, uv + colOff).b; - // 出力 - return float4(tex.rgb, 1.0); + return outCol; } ' @@ -19018,47 +24638,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBS3dSwapTransitionShader { +function Get-OBSGlitchShader { -[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] +[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] param( -# Set the image_a of OBS3dSwapTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBS3dSwapTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBS3dSwapTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBS3dSwapTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the reflection of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('reflection')] -[Single] -$Reflection, -# Set the perspective of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('perspective')] +# Set the AMT of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('AMT')] [Single] -$Perspective, -# Set the depth of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('depth')] +$AMT, +# Set the SPEED of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('SPEED')] [Single] -$Depth, -# Set the background_color of OBS3dSwapTransitionShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[String] -$BackgroundColor, +$SPEED, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -19089,108 +24680,81 @@ $UseShaderTime process { -$shaderName = '3d_swap_transition' -$ShaderNoun = 'OBS3dSwapTransitionShader' +$shaderName = 'glitch' +$ShaderNoun = 'OBSGlitchShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MlXGzf - -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -uniform float reflection< - string label = "Reflection (0.4)"; +//based on https://www.shadertoy.com/view/MtXBDs +//inputs +uniform float AMT< + string label = "AMT"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; + float minimum = 0.0; + float maximum = 1.0; float step = 0.01; -> = 0.4; -uniform float perspective< - string label = "Perspective (0.2)"; +> = 0.2; //0 - 1 glitch amount +uniform float SPEED< + string label = "Speed"; string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; + float minimum = 0.0; + float maximum = 1.0; float step = 0.01; -> = .2; -uniform float depth< - string label = "Depth (3.0)"; - string widget_type = "slider"; - float minimum = 1.00; - float maximum = 10.00; - float step = 0.1; -> = 3.; - -#ifndef OPENGL -#define lessThan(a,b) (a < b) -#endif - +> = 0.6; //0 - 1 speed -uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; - -bool inBounds (float2 p) { - return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); +//2D (returns 0 - 1) +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); } - -float2 project (float2 p) { - return p * float2(1.0, -1.2) + float2(0.0, 2.22); + +float randomRange (in float2 seed, in float min, in float max) { + return min + random2d(seed) * (max - min); } - -float4 bgColor (float2 p, float2 pfr, float2 pto) { - float4 c = background_color; - pfr = project(pfr); - if (inBounds(pfr)) { - c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); - } - pto = project(pto); - if (inBounds(pto)) { - c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); - } - return c; + +// return 1 if v inside 1d range +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); } - -float4 mainImage(VertData v_in) : TARGET { - float2 p = v_in.uv; - float2 pfr = float2(-1.,-1.); - float2 pto = float2(-1.,-1.); - - float progress = transition_time; - float size = lerp(1.0, depth, progress); - float persp = perspective * progress; - pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); - - size = lerp(1.0, depth, 1.-progress); - persp = perspective * (1.-progress); - pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); - - bool fromOver = progress < 0.5; - float4 rgba = background_color; - if (fromOver) { - if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); - } - else if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else { - rgba = bgColor(p, pfr, pto); - } - } - else { - if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); + + + +float4 mainImage(VertData v_in) : TARGET +{ + + float time = floor(elapsed_time * SPEED * 60.0); + float2 uv = v_in.uv; + + //copy orig + float4 outCol = image.Sample(textureSampler, uv); + + //randomly offset slices horizontally + float maxOffset = AMT/2.0; + for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { + float sliceY = random2d(float2(time , 2345.0 + float(i))); + float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; + float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); + float2 uvOff = uv; + uvOff.x += hOffset; + if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ + outCol = image.Sample(textureSampler, uvOff); + } } - else { - rgba = bgColor(p, pfr, pto); + + //do slight offset on one entire channel + float maxColOffset = AMT/6.0; + float rnd = random2d(float2(time , 9545.0)); + float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), + randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); + if (rnd < 0.33){ + outCol.r = image.Sample(textureSampler, uv + colOffset).r; + + }else if (rnd < 0.66){ + outCol.g = image.Sample(textureSampler, uv + colOffset).g; + + } else{ + outCol.b = image.Sample(textureSampler, uv + colOffset).b; } - } - if (convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; + + return outCol; } ' } @@ -19289,15 +24853,42 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAddShader { +function Get-OBSGlowShader { -[Alias('Set-OBSAddShader','Add-OBSAddShader')] +[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] param( -# Set the other_image of OBSAddShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] +# Set the glow_percent of OBSGlowShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSGlowShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSGlowShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSGlowShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed of OBSGlowShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGlowShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the notes of OBSGlowShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$OtherImage, +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -19328,17 +24919,103 @@ $UseShaderTime process { -$shaderName = 'Add' -$ShaderNoun = 'OBSAddShader' +$shaderName = 'glow' +$ShaderNoun = 'OBSGlowShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int glow_percent< + string label = "Glow percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed< + string label = "Pulse speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform string notes< + string widget_type = "info"; +> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; + + +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; +} + +float BlurStyler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; +} float4 mainImage(VertData v_in) : TARGET { - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return clamp(base + other, 0.0, 1.0); + float2 offsets[4]; + offsets[0] = float2(-0.1, 0.125); + offsets[1] = float2(-0.1, -0.125); + offsets[2] = float2(0.1, -0.125); + offsets[3] = float2(0.1, 0.125); + + // convert input for vector math + float4 col = image.Sample(textureSampler, v_in.uv); + float blur_amount = float(blur) /100.0; + float glow_amount = float(glow_percent) * 0.01; + float speed = float(pulse_speed) * 0.01; + float luminance_floor = float(min_brightness) /100.0; + float luminance_ceiling = float(max_brightness) /100.0; + + if (col.a > 0.0) + { + //circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + + // simple glow calc + for (int n = 0; n < 4; n++) { + b = BlurStyler(t, 0, c, d, ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + col += (ncolor * (glow_amount * b)); + } + } + } + return col; + } ' @@ -19438,25 +25115,96 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaBorderShader { +function Get-OBSGradientShader { -[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] +[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] param( -# Set the border_color of OBSAlphaBorderShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +# Set the start_color of OBSGradientShader +[Alias('start_color')] +[ComponentModel.DefaultBindingProperty('start_color')] [String] -$BorderColor, -# Set the border_thickness of OBSAlphaBorderShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the alpha_cut_off of OBSAlphaBorderShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +$StartColor, +# Set the start_step of OBSGradientShader +[Alias('start_step')] +[ComponentModel.DefaultBindingProperty('start_step')] [Single] -$AlphaCutOff, +$StartStep, +# Set the middle_color of OBSGradientShader +[Alias('middle_color')] +[ComponentModel.DefaultBindingProperty('middle_color')] +[String] +$MiddleColor, +# Set the middle_step of OBSGradientShader +[Alias('middle_step')] +[ComponentModel.DefaultBindingProperty('middle_step')] +[Single] +$MiddleStep, +# Set the end_color of OBSGradientShader +[Alias('end_color')] +[ComponentModel.DefaultBindingProperty('end_color')] +[String] +$EndColor, +# Set the end_step of OBSGradientShader +[Alias('end_step')] +[ComponentModel.DefaultBindingProperty('end_step')] +[Single] +$EndStep, +# Set the alpha_percent of OBSGradientShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the pulse_speed of OBSGradientShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGradientShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the rotate_colors of OBSGradientShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the horizontal of OBSGradientShader +[ComponentModel.DefaultBindingProperty('horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the vertical of OBSGradientShader +[ComponentModel.DefaultBindingProperty('vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the gradient_center_width_percentage of OBSGradientShader +[Alias('gradient_center_width_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] +[Int32] +$GradientCenterWidthPercentage, +# Set the gradient_center_height_percentage of OBSGradientShader +[Alias('gradient_center_height_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] +[Int32] +$GradientCenterHeightPercentage, +# Set the notes of OBSGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -19487,44 +25235,209 @@ $UseShaderTime process { -$shaderName = 'alpha_border' -$ShaderNoun = 'OBSAlphaBorderShader' +$shaderName = 'gradient' +$ShaderNoun = 'OBSGradientShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 border_color< - string label = "Border color"; -> = {0.0,0.0,0.0,1.0}; -uniform int border_thickness< - string label = "Border thickness"; +// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; +uniform float start_step< + string label = "Start step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.15; +uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; +uniform float middle_step< + string label = "Middle step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.4; +uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; +uniform float end_step< + string label = "End step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform int alpha_percent< + string label = "Alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int pulse_speed< + string label = "Pulse speed"; string widget_type = "slider"; int minimum = 0; int maximum = 100; int step = 1; > = 0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; +uniform bool ease; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = true; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform bool horizontal; +uniform bool vertical; +uniform int gradient_center_width_percentage< + string label = "gradient center width percentage"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int gradient_center_height_percentage< + string label = "gradient center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; + +float EaseInOutCircTimer(float t, float b, float c, float d) { + t /= d / 2; + if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; +} + +float BlurStyler(float t, float b, float c, float d, bool ease) +{ + if (ease) return EaseInOutCircTimer(t, 0, c, d); + return t; +} + +struct gradient +{ + float4 color; + float step; +}; + float4 mainImage(VertData v_in) : TARGET { - float4 pix = image.Sample(textureSampler, v_in.uv); - if (pix.a > alpha_cut_off) - return pix; - [loop] for(int x = -border_thickness;x alpha_cut_off) - return border_color; - } - } - } - return pix; + const float PI = 3.14159265f;//acos(-1); + float speed = float(pulse_speed) * 0.01; + float alpha = float(alpha_percent) * 0.01; + + //circular easing variable + float t = sin(elapsed_time * speed) * 2 - 1; + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + + float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); + float4 color = image.Sample(textureSampler, v_in.uv); + float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; + float4 gray = float4(luminance,luminance,luminance, 1); + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + b = BlurStyler(t, 0, c, d, ease); + + const int no_colors = 3; + float4 s_color = start_color; + float4 m_color = middle_color; + float4 e_color = end_color; + + if (rotate_colors) + { + // get general time number between 0 and 4 + float tx = (b + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls + + if (tx <= 2.0) + { + s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + } + + if ((tx >= 1.0) && (tx <= 3.0)) + { + s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + } + + if (tx >= 2.0) + { + s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + } + + if (tx < 0) + { + s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + } + } + + float4 colors[no_colors]; + colors[0] =s_color; + colors[1] = m_color; + colors[2] = e_color; + float step[no_colors]; + step[0] = start_step; + step[1] = middle_step; + step[2] = end_step; + + float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); + float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); + float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); + + float dist = distance(v_in.uv, gradient_center); + if (horizontal && (vertical == false)) + { + dist = distance(v_in.uv.y, gradient_center.y); + } + if (vertical && (horizontal == false)) + { + dist = distance(v_in.uv.x, gradient_center.x); + } + + float4 col = colors[0]; + for (int i = 1; i < no_colors; ++i) { + col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); + } + col.a = clamp(alpha, 0.0, 1.0); + if (Apply_To_Alpha_Layer == false) + color.a = alpha; + if (Apply_To_Specific_Color) + { + col.a = alpha; + float4 original_color = image.Sample(textureSampler, v_in.uv); + col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; + } + // result = float4(redness, greenness,blueness,1); + //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); + //color.rgb += col * alpha; + //color.a += clamp(1.0 - alpha, 0.0,1.0); + ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); + //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); + color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); + + } + return color; + + } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -19622,60 +25535,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaGamingBentCameraShader { +function Get-OBSHalftoneShader { -[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] +[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] param( -# Set the left_side_width of OBSAlphaGamingBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSAlphaGamingBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSAlphaGamingBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSAlphaGamingBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSAlphaGamingBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] -[Single] -$RightSideShadow, -# Set the right_flip_width of OBSAlphaGamingBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] -[Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +# Set the threshold of OBSHalftoneShader +[ComponentModel.DefaultBindingProperty('threshold')] [Single] -$RightFlipShadow, +$Threshold, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -19706,153 +25573,90 @@ $UseShaderTime process { -$shaderName = 'alpha-gaming-bent-camera' -$ShaderNoun = 'OBSAlphaGamingBentCameraShader' +$shaderName = 'halftone' +$ShaderNoun = 'OBSHalftoneShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; +#define PI 3.1415926535897932384626433832795 +#define PI180 float(PI / 180.0) -uniform float right_side_width< - string label = "Right side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; +uniform float threshold< + string label = "Threshold"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; + float step = 0.001; > = 0.6; -float4 mainImage(VertData v_in) : TARGET +float sind(float a) { - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; - } - - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; - } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; + return sin(a * PI180); } -' + +float cosd(float a) +{ + return cos(a * PI180); } -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' + +float added(float2 sh, float sa, float ca, float2 c, float d) +{ + return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); } -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + +float4 mainImage(VertData v_in) : TARGET +{ + // Halftone dot matrix shader + // @author Tomek Augustyn 2010 + + // Ported from my old PixelBender experiment + // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk + + float coordX = v_in.uv.x; + float coordY = v_in.uv.y; + float2 dstCoord = float2(coordX, coordY); + float2 rotationCenter = float2(0.5, 0.5); + float2 shift = dstCoord - rotationCenter; + + float dotSize = 3.0; + float angle = 45.0; + + float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); + float4 srcPixel = image.Sample(textureSampler, dstCoord); + + float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; + float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); + + // uncomment to see how the raster pattern looks + // gray = rasterPattern; + + return float4(gray, gray, gray, 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } } 'Remove' { @@ -19924,57 +25728,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedPathShader { +function Get-OBSHardBlinkShader { -[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] +[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] param( -# Set the ViewProj of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedPathShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the timeon of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeon')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedPathShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedPathShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedPathShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedPathShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$Timeon, +# Set the timeoff of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeoff')] [Single] -$RandF, -# Set the speed_percent of OBSAnimatedPathShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the path_map of OBSAnimatedPathShader -[Alias('path_map')] -[ComponentModel.DefaultBindingProperty('path_map')] -[String] -$PathMap, -# Set the reverse of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, +$Timeoff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -20005,98 +25770,40 @@ $UseShaderTime process { -$shaderName = 'animated_path' -$ShaderNoun = 'OBSAnimatedPathShader' +$shaderName = 'hard_blink' +$ShaderNoun = 'OBSHardBlinkShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; - -uniform int speed_percent = 100; -uniform texture2d path_map; -uniform bool reverse = false; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -float4 convert_pmalpha(float4 c) -{ - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - float3 pos = v_in.pos.xyz; - float3 current_pos; - float speed = speed_percent * 0.01; - //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float t = 1.0 + sin(elapsed_time * speed) ; - // combine luma texture and user defined shine color - float luma = path_map.Sample(textureSampler, v_in.uv).x; - if (reverse) - { - luma = 1.0 - luma; - } - - float time = lerp(0.0f, 1.0f , t - 1.0); - - // set current position in time - current_pos.x = 0; - current_pos.y = 0; - +// hard_blink shader created by https://github.com/WhazzItToYa +// +// Periodically makes the source image 100% transparent, in configurable intervals. - float2 offset = uv_offset; - if (speed == 0.0f) - { - offset.x = 0.0f; - offset.y = 0.0f; - } - else - { - offset.x = uv_offset.x + time * luma; - offset.y = uv_offset.y + time * luma; - } +uniform float timeon< + string label = "Time On"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} +uniform float timeoff< + string label = "Time Off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - return image.Sample(textureSampler, v_in.uv); -} - -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } + float4 color = image.Sample(textureSampler, v_in.uv); + float m = timeon + timeoff; + float t = elapsed_time % m; + if (t < timeon) { + return color; + } else { + return float4(color.r, color.g, color.b, 0.0); + } } ' @@ -20196,119 +25903,26 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedTextureShader { +function Get-OBSHeatWaveSimpleShader { -[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] +[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] param( -# Set the ViewProj of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSAnimatedTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Image of OBSAnimatedTextureShader -[Alias('Animation_Image')] -[ComponentModel.DefaultBindingProperty('Animation_Image')] -[String] -$AnimationImage, -# Set the Colorization_Image of OBSAnimatedTextureShader -[Alias('Colorization_Image')] -[ComponentModel.DefaultBindingProperty('Colorization_Image')] -[String] -$ColorizationImage, -# Set the reverse of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the bounce of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('bounce')] -[Management.Automation.SwitchParameter] -$Bounce, -# Set the center_animation of OBSAnimatedTextureShader -[Alias('center_animation')] -[ComponentModel.DefaultBindingProperty('center_animation')] -[Management.Automation.SwitchParameter] -$CenterAnimation, -# Set the polar_animation of OBSAnimatedTextureShader -[Alias('polar_animation')] -[ComponentModel.DefaultBindingProperty('polar_animation')] -[Management.Automation.SwitchParameter] -$PolarAnimation, -# Set the polar_angle of OBSAnimatedTextureShader -[Alias('polar_angle')] -[ComponentModel.DefaultBindingProperty('polar_angle')] -[Single] -$PolarAngle, -# Set the polar_height of OBSAnimatedTextureShader -[Alias('polar_height')] -[ComponentModel.DefaultBindingProperty('polar_height')] -[Single] -$PolarHeight, -# Set the speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] -[Single] -$SpeedHorizontalPercent, -# Set the speed_vertical_percent of OBSAnimatedTextureShader -[Alias('speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] +# Set the Rate of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Rate')] [Single] -$SpeedVerticalPercent, -# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('tint_speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] +$Rate, +# Set the Strength of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] [Single] -$TintSpeedHorizontalPercent, -# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader -[Alias('tint_speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] +$Strength, +# Set the Distortion of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Distortion')] [Single] -$TintSpeedVerticalPercent, -# Set the Alpha of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('Alpha')] +$Distortion, +# Set the Opacity of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Opacity')] [Single] -$Alpha, -# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader -[Alias('Use_Animation_Image_Color')] -[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] -[Management.Automation.SwitchParameter] -$UseAnimationImageColor, +$Opacity, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -20339,172 +25953,74 @@ $UseShaderTime process { -$shaderName = 'animated_texture' -$ShaderNoun = 'OBSAnimatedTextureShader' +$shaderName = 'heat-wave-simple' +$ShaderNoun = 'OBSHeatWaveSimpleShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 -// Animates a texture with polar sizing and color options -// for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes; - -uniform texture2d Animation_Image; -uniform texture2d Colorization_Image; -uniform bool reverse = false; -uniform bool bounce = false; -uniform bool center_animation = true; -uniform bool polar_animation = true; -uniform float polar_angle = 90.0; -uniform float polar_height = 1.0; -uniform float speed_horizontal_percent = 50; -uniform float speed_vertical_percent = 5; -uniform float tint_speed_horizontal_percent = 50; -uniform float tint_speed_vertical_percent = 5; -uniform float Alpha = 1.0; -uniform bool Use_Animation_Image_Color = true; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +// Heat Wave Simple, Version 0.03, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// +// Description: +// Generate a crude pseudo heat wave displacement on an image source. +// +// Based on: https://www.shadertoy.com/view/td3GRn by Dombass +// +// Changelog: +// 0.03 - Added Opacity control +// 0.02 - Added crude Rate, Strength, and Distortion controls +// 0.01 - Initial release -float4 convert_pmalpha(float4 color) -{ - float4 ret = color; - if (color.a >= 0.001) - ret.xyz /= color.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} +uniform float Rate< + string label = "Rate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 5.0; +uniform float Strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = -25.0; + float maximum = 25.0; + float step = 0.01; +> = 1.0; +uniform float Distortion< + string label = "Distortion"; + string widget_type = "slider"; + float minimum = 3.0; + float maximum = 20.0; + float step = 0.01; +> = 10.0; +uniform float Opacity< + string label = "Opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.00; -float2 time(float2 speed_dir) +float4 mainImage( VertData v_in ) : TARGET { - float PI = 3.1415926535897932384626433832795; //acos(-1); - - float2 t = (elapsed_time * speed_dir) ; - if (bounce) - { - // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen - t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; - t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; - t *= -0.5; - } + float2 uv = v_in.uv; + float distort = clamp(Distortion, 3.0, 20.0); - if (reverse) - t = t * -1; - return t; + // Time varying pixel color + float jacked_time = Rate*elapsed_time; + float2 scale = float2(0.5, 0.5); + float str = clamp(Strength, -25.0, 25.0) * 0.01; + + uv += str * sin(scale*jacked_time + length( uv ) * distort); + float4 c = image.Sample( textureSampler, uv); + c.a *= saturate(Opacity*0.01); + return c; } -VertData mainTransform(VertData v_in) -{ - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); - - VertData vert_out; - //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); - - float2 offset = uv_offset; - - if (center_animation) - { - vert_out.uv = v_in.uv - 0.5f; - } - else - { - offset += time(speed_dir); - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - } - - return vert_out; -} - - -float4 mainImage(VertData v_in) : TARGET -{ - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); - float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); - - //compensate for background vertex shader values - float2 background_offset = float2(-.5,-.5); - if (!center_animation) - background_offset = time(speed_dir); - float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); - - // Convert our texture coordinates to polar form: - if (polar_animation) { - - float2 polar = float2( - atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle - log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius - ); - - // Check how much our texture sampling point changes between - // neighbouring pixels to the sides (ddx) and above/below (ddy) - ///float4 gradient = float4(ddx(polar), ddy(polar)); - - // If our angle wraps around between adjacent samples, - // discard one full rotation from its value and keep the fraction. - ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; - - float2 tintUVs = polar * 4; - tintUVs += time(tint_speed_dir); - - // Apply texture scale - polar *= 4; - // Scroll the texture over time. - polar += time(speed_dir); - float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); - - - float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); - float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); - //animation.a = clamp((intensity),0.0,1.0); - if (Use_Animation_Image_Color) - { - animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - else - { - animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - //if (keyAmount > 0.5f) - rgba = lerp(rgba, animation, animation.a * Alpha); - } - - return rgba; -} - -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} - -' +' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { @@ -20601,32 +26117,75 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAsciiShader { +function Get-OBSHexagonShader { -[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] +[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] param( -# Set the scale of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('scale')] -[Int32] -$Scale, -# Set the base_color of OBSAsciiShader -[Alias('base_color')] -[ComponentModel.DefaultBindingProperty('base_color')] +# Set the Hex_Color of OBSHexagonShader +[Alias('Hex_Color')] +[ComponentModel.DefaultBindingProperty('Hex_Color')] [String] -$BaseColor, -# Set the monochrome of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('monochrome')] +$HexColor, +# Set the Alpha_Percent of OBSHexagonShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Int32] +$AlphaPercent, +# Set the Quantity of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Quantity')] +[Single] +$Quantity, +# Set the Border_Width of OBSHexagonShader +[Alias('Border_Width')] +[ComponentModel.DefaultBindingProperty('Border_Width')] +[Int32] +$BorderWidth, +# Set the Blend of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Blend')] [Management.Automation.SwitchParameter] -$Monochrome, -# Set the character_set of OBSAsciiShader -[Alias('character_set')] -[ComponentModel.DefaultBindingProperty('character_set')] +$Blend, +# Set the Equilateral of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Equilateral')] +[Management.Automation.SwitchParameter] +$Equilateral, +# Set the Zoom_Animate of OBSHexagonShader +[Alias('Zoom_Animate')] +[ComponentModel.DefaultBindingProperty('Zoom_Animate')] +[Management.Automation.SwitchParameter] +$ZoomAnimate, +# Set the Speed_Percent of OBSHexagonShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] [Int32] -$CharacterSet, -# Set the note of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('note')] +$SpeedPercent, +# Set the Glitch of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Distort_X of OBSHexagonShader +[Alias('Distort_X')] +[ComponentModel.DefaultBindingProperty('Distort_X')] +[Single] +$DistortX, +# Set the Distort_Y of OBSHexagonShader +[Alias('Distort_Y')] +[ComponentModel.DefaultBindingProperty('Distort_Y')] +[Single] +$DistortY, +# Set the Offset_X of OBSHexagonShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSHexagonShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the notes of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$Note, +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -20657,110 +26216,132 @@ $UseShaderTime process { -$shaderName = 'ascii' -$ShaderNoun = 'OBSAsciiShader' +$shaderName = 'hexagon' +$ShaderNoun = 'OBSHexagonShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 -// https://github.com/Oncorporation/obs-shaderfilter -// Based on the following shaders: -// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 -// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 - -// Modifications of original shaders include: -// - Porting from GLSL to HLSL -// - Combining characters sets from both source shaders -// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set -// -// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ -// converts a bitmap into int then decodes it to look like text - -uniform int scale< - string label = "Scale"; +// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 Hex_Color; +uniform int Alpha_Percent< + string label = "Alpha percent"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; + int minimum = 0; + int maximum = 100; int step = 1; -> = 1; // Size of characters -uniform float4 base_color< - string label = "Base color"; -> = {0.0,1.0,0.0,1.0}; // Monochrome base color -uniform bool monochrome< - string label = "Monochrome"; -> = false; -uniform int character_set< - string label = "Character set"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Large set of non-letters"; - int option_1_value = 1; - string option_1_label = "Small set of capital letters"; -> = 0; -uniform string note< +> = 100; +uniform float Quantity< + string label = "Quantity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 25; +uniform int Border_Width< + string label = "Border Width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 115; + int step = 1; +> = 15; // <- -15 to 85, -15 off top +uniform bool Blend; +uniform bool Equilateral; +uniform bool Zoom_Animate; +uniform int Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool Glitch; +uniform float Distort_X< + string label = "Distort X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Distort_Y< + string label = "Distort Y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Offset_Y< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform string notes< string widget_type = "info"; -> = "Base color is used as monochrome base color."; +>= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; -float character(int n, float2 p) +float mod(float x, float y) { - p = floor(p*float2(4.0, 4.0) + 2.5); - if (clamp(p.x, 0.0, 4.0) == p.x) - { - if (clamp(p.y, 0.0, 4.0) == p.y) - { - int a = int(round(p.x) + 5.0 * round(p.y)); - if (((n >> a) & 1) == 1) return 1.0; - } - } - return 0.0; + return x - y * floor(x/y); } -float2 mod(float2 x, float2 y) +float2 mod2(float2 x, float2 y) { return x - y * floor(x/y); } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 iResolution = uv_size*uv_scale; - float2 pix = v_in.pos.xy; - float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); +// 0 on edges, 1 in non_edge +float hex(float2 p) { + float xyratio = 1; + if (Equilateral) + xyratio = uv_size.x /uv_size.y; - float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; - - int n; - int charset = clamp(character_set, 0, 1); + // calc p + p.x = mul(p.x,xyratio); + p.y += mod(floor(p.x) , 2.0)*0.5; + p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); + return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); +} - if (charset==0) - { - if (gray <= 0.2) n = 4096; // . - if (gray > 0.2) n = 65600; // : - if (gray > 0.3) n = 332772; // * - if (gray > 0.4) n = 15255086; // o - if (gray > 0.5) n = 23385164; // & - if (gray > 0.6) n = 15252014; // 8 - if (gray > 0.7) n = 13199452; // @ - if (gray > 0.8) n = 11512810; // # - } - else if (charset==1) - { - if (gray <= 0.1) n = 0; - if (gray > 0.1) n = 9616687; // R - if (gray > 0.3) n = 32012382; // S - if (gray > 0.5) n = 16303663; // D - if (gray > 0.7) n = 15255086; // O - if (gray > 0.8) n = 16301615; // B - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); + float alpha = float(Alpha_Percent) * 0.01; + float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); + float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; + float speed = float(Speed_Percent) * 0.01; + float time = (1 + sin(elapsed_time * speed))*0.5; + if (Zoom_Animate) + quantity *= time; - float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); - - if (monochrome) - { - c.rgb = base_color.rgb; - } - c = c*character(n, p); - - return c; -} + // create a (pos)ition reference, hex radius and smoothstep out the non_edge + float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); + if (Glitch) + quantity *= lerp(pos.x, pos.y, rand_f); + float2 p = (pos * quantity); // number of hexes to be created + float r = (1.0 -0.7)*0.5; // cell default radius + float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge + + // make the border colorable - non_edge is scaled + float4 c = float4(non_edge, non_edge,non_edge,1.0) ; + if (non_edge < 1) + { + c = Hex_Color; + c.a = alpha; + if (Blend) + c = lerp(rgba, c, 1 - non_edge); + return lerp(rgba,c,alpha); + } + return lerp(rgba, c * rgba, alpha); +} ' } @@ -20859,56 +26440,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAspectRatioShader { +function Get-OBSHslHsvSaturationShader { -[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] +[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] param( -# Set the ViewProj of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAspectRatioShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the hslSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSAspectRatioShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAspectRatioShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAspectRatioShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAspectRatioShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$HslSaturationFactor, +# Set the hslGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslGamma')] [Single] -$RandF, -# Set the uv_size of OBSAspectRatioShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the borderColor of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('borderColor')] -[String] -$BorderColor, -# Set the notes of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$HslGamma, +# Set the hsvSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] +[Single] +$HsvSaturationFactor, +# Set the hsvGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvGamma')] +[Single] +$HsvGamma, +# Set the adjustmentOrder of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('adjustmentOrder')] +[Int32] +$AdjustmentOrder, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -20939,101 +26494,171 @@ $UseShaderTime process { -$shaderName = 'aspect_ratio' -$ShaderNoun = 'OBSAspectRatioShader' +$shaderName = 'hsl_hsv_saturation' +$ShaderNoun = 'OBSHslHsvSaturationShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; +// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions -// variables -uniform float4 borderColor = {0,0,0,0}; -float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; -uniform string notes; +uniform float hslSaturationFactor< + string label = "HSL Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; +uniform float hslGamma< + string label = "HSL Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +uniform float hsvSaturationFactor< + string label = "HSV Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; +uniform float hsvGamma< + string label = "HSV Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; - float2 hw = uv_scale; - // determine the game window''s current aspect ratio - float windowaspect = hw.x / hw.y; +uniform int adjustmentOrder< + string label = "Order"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; + int option_1_value = 2; + string option_1_label = "HSL first, then HSV"; + int option_2_value = 3; + string option_2_label = "HSV first, then HSL"; +> = 1; - // current viewport height should be scaled by this amount - float scaleheight = windowaspect / targetaspect; +// HSV conversion +float3 rgb2hsv(float3 c) { + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} - // if scaled height is less than current height, add letterbox - if (scaleheight < 1.0f) - { - Rect rect = camera.rect; +float3 hsv2rgb(float3 c) { + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} - rect.width = 1.0f; - rect.height = scaleheight; - rect.x = 0; - rect.y = (1.0f - scaleheight) / 2.0f; +// HSL conversion - camera.rect = rect; - } - else // add pillarbox - { - float scalewidth = 1.0f / scaleheight; +float3 rgb2hsl(float3 c) { + float maxVal = max(c.r, max(c.g, c.b)); + float minVal = min(c.r, min(c.g, c.b)); + float delta = maxVal - minVal; + float h = 0.0; + float s = 0.0; + float l = (maxVal + minVal) / 2.0; - Rect rect = camera.rect; + if(delta != 0) { + if(l < 0.5) s = delta / (maxVal + minVal); + else s = delta / (2.0 - maxVal - minVal); - rect.width = scalewidth; - rect.height = 1.0f; - rect.x = (1.0f - scalewidth) / 2.0f; - rect.y = 0; + if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); + else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; + else h = (c.r - c.g) / delta + 4.0; - camera.rect = rect; - } - return vert_out; + h /= 6.0; + } + + return float3(h, s, l); } -float4 mainImage(VertData v_in) : TARGET -{ - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) - { - return borderColor; - } - else - { - return image.Sample(textureSampler, v_in.uv); - } +float hue2rgb(float p, float q, float t) { + if(t < 0.0) t += 1.0; + if(t > 1.0) t -= 1.0; + if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if(t < 1.0/2.0) return q; + if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; } -technique Draw +float3 hsl2rgb(float3 c) { + float r, g, b; + + if(c.y == 0.0) { + r = g = b = c.z; + } else { + float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; + float p = 2.0 * c.z - q; + r = hue2rgb(p, q, c.x + 1.0/3.0); + g = hue2rgb(p, q, c.x); + b = hue2rgb(p, q, c.x - 1.0/3.0); + } + + return float3(r, g, b); +} + +float3 adjustColorWithOrder(float3 originalColor) { + if (adjustmentOrder == 1) { + // Parallel adjustment (both HSL and HSV operate on the original image and then blend) + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 hslAdjustedColor = hsl2rgb(hslAdjusted); + + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); + + float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; + return finalColor; + } + else if (adjustmentOrder == 2) { + // HSL first, then HSV + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 afterHSL = hsl2rgb(hslAdjusted); + float3 hsvAdjusted = rgb2hsv(afterHSL); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + return hsv2rgb(hsvAdjusted); + } + else if (adjustmentOrder == 3) { + // HSV first, then HSL + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 afterHSV = hsv2rgb(hsvAdjusted); + float3 hslAdjusted = rgb2hsl(afterHSV); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + return hsl2rgb(hslAdjusted); + } + return originalColor; // Default to original color in case of unexpected values +} + +// Final composite + +float4 mainImage(VertData v_in) : TARGET { - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } + float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; + float3 adjustedColor = adjustColorWithOrder(originalColor); + return float4(adjustedColor, 1.0); // preserving the original alpha } ' @@ -21133,78 +26758,19 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBackgroundRemovalShader { +function Get-OBSHueRotatonShader { -[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] +[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] param( -# Set the ViewProj of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSBackgroundRemovalShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSBackgroundRemovalShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSBackgroundRemovalShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSBackgroundRemovalShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSBackgroundRemovalShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSBackgroundRemovalShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the target of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('target')] -[String] -$Target, -# Set the color of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the opacity of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('opacity')] +# Set the Speed of OBSHueRotatonShader +[ComponentModel.DefaultBindingProperty('Speed')] [Single] -$Opacity, -# Set the invert of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the Convert_709to601 of OBSBackgroundRemovalShader -[Alias('Convert_709to601')] -[ComponentModel.DefaultBindingProperty('Convert_709to601')] -[Management.Automation.SwitchParameter] -$Convert709to601, -# Set the Convert_601to709 of OBSBackgroundRemovalShader -[Alias('Convert_601to709')] -[ComponentModel.DefaultBindingProperty('Convert_601to709')] +$Speed, +# Set the Hue_Override of OBSHueRotatonShader +[Alias('Hue_Override')] +[ComponentModel.DefaultBindingProperty('Hue_Override')] [Management.Automation.SwitchParameter] -$Convert601to709, +$HueOverride, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -21235,124 +26801,103 @@ $UseShaderTime process { -$shaderName = 'background_removal' -$ShaderNoun = 'OBSBackgroundRemovalShader' +$shaderName = 'hue-rotaton' +$ShaderNoun = 'OBSHueRotatonShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; - -uniform texture2d target; -uniform float4 color; -uniform float opacity = 15.0; -uniform bool invert; -uniform bool Convert_709to601; -uniform bool Convert_601to709; - +// Hue Rotation shader, version 1.0 for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Description: +// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue +// override option is provided to force a specific rotating hue instead of the original image''s hue. +// +// Changelog: +// 1.0 - Initial release -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 10.00; +uniform bool Hue_Override = false; -float dot(float3 a,float3 b){ - return a.x*b.x+a.y*b.y+a.z*b.z; +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); } -//BT.601 to BT.709 -// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input -// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. -float4 Convert601to709(float4 rgba) +#define Epsilon 1e-10 + +float3 RGBtoHCV(in float3 RGB) { - float3 s1 = rgba.rgb; - s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace - return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace + // Based on work by Sam Hocevar and Emil Persson + float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); + float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); + float C = Q.x - min(Q.w, Q.y); + float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); + return float3(H, C, Q.x); } -//BT.709 to BT.601 -float4 Convert709to601(float4 rgba) +float3 HSVtoRGB(in float3 HSV) { - float3 s1 = rgba.rgb; - s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); - return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; + float3 RGB = HUEtoRGB(HSV.x); + return ((RGB - 1) * HSV.y + 1) * HSV.z; } -VertDataOut VSDefault(VertDataIn v_in) +float3 RGBtoHSV(in float3 RGB) { - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); - vert_out.uv = v_in.uv; - vert_out.uv2 = v_in.uv * uv_scale + uv_offset; - return vert_out; + float3 HCV = RGBtoHCV(RGB); + float S = HCV.y / (HCV.z + Epsilon); + return float3(HCV.x, S, HCV.z); } -float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET +float4 mainImage( VertData v_in ) : TARGET { - float Tolerance = opacity * 0.01; - float4 rgba = image.Sample(textureSampler, v_in.uv); - - float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; - if (invert){ - targetRGB.rgb = 1.0 - targetRGB.rgb; - } - if (Convert_709to601) - { - rgba.rgb = Convert709to601(rgba).rgb; - targetRGB.rgb = Convert709to601(targetRGB).rgb; - } - - if (Convert_601to709) - { - rgba.rgb = Convert601to709(rgba).rgb; - targetRGB.rbg = Convert601to709(targetRGB).rgb; - } - - float4 shadowRGB = targetRGB * targetRGB; + float2 uv = v_in.uv; + float4 col_in = image.Sample(textureSampler, uv); + float3 col_out; + float3 HSV = RGBtoHSV(col_in.rgb); - if ((abs(targetRGB.r - rgba.r) <= Tolerance && - abs(targetRGB.g - rgba.g) <= Tolerance && - abs(targetRGB.b - rgba.b) <= Tolerance) - || (abs(shadowRGB.r - rgba.r) <= Tolerance && - abs(shadowRGB.g - rgba.g) <= Tolerance && - abs(shadowRGB.b - rgba.b) <= Tolerance)) - { - rgba.rgba = float4(0,0,0,0); - } - return rgba; -} + if(Hue_Override) + HSV.x = elapsed_time * Speed * 0.01; + else + HSV.x += elapsed_time * Speed * 0.01; -technique Draw -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSColorMaskRGBA(v_in); - } + // Normalize Hue + HSV.x = frac(HSV.x); + + col_out = HSVtoRGB(HSV); + return float4(col_out, col_in.a); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -21450,50 +26995,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlendOpacityShader { +function Get-OBSIntensityScopeShader { -[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] +[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] param( -# Set the Vertical of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the Rotational of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Rotational')] -[Management.Automation.SwitchParameter] -$Rotational, -# Set the Rotation_Offset of OBSBlendOpacityShader -[Alias('Rotation_Offset')] -[ComponentModel.DefaultBindingProperty('Rotation_Offset')] -[Single] -$RotationOffset, -# Set the Opacity_Start_Percent of OBSBlendOpacityShader -[Alias('Opacity_Start_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] -[Single] -$OpacityStartPercent, -# Set the Opacity_End_Percent of OBSBlendOpacityShader -[Alias('Opacity_End_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] -[Single] -$OpacityEndPercent, -# Set the Spread of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Spread')] +# Set the gain of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('gain')] [Single] -$Spread, -# Set the Speed of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Speed')] +$Gain, +# Set the blend of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('blend')] [Single] -$Speed, -# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Notes of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, +$Blend, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -21524,92 +27037,61 @@ $UseShaderTime process { -$shaderName = 'blend_opacity' -$ShaderNoun = 'OBSBlendOpacityShader' +$shaderName = 'intensity-scope' +$ShaderNoun = 'OBSIntensityScopeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 14, 2022 -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.01; -> = 0.0; -uniform float Opacity_Start_Percent< - string label = "Opacity Start Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 0.0; -uniform float Opacity_End_Percent< - string label = "Opacity End Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform float Spread< - string label = "Spread"; +// Robin Green, Dec 2016 +// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro +uniform float gain< + string label = "Gain"; string widget_type = "slider"; - float minimum = 0.25; - float maximum = 10.0; + float minimum = 0.01; + float maximum = 1.00; float step = 0.01; -> = 0.5; -uniform float Speed< - string label = "Speed"; +> = 0.3; +uniform float blend< + string label = "Blend"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; + float minimum = 0.00; + float maximum = 1.00; float step = 0.01; -> = 0.0; -uniform bool Apply_To_Alpha_Layer = true; -uniform string Notes< - string widget_type = "info"; -> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; +> = 0.6; float4 mainImage(VertData v_in) : TARGET { - float4 point_color = image.Sample(textureSampler, v_in.uv); - float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; - float4 gray = float4(luminance,luminance,luminance, 1); - - float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); - - if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) - { - //set opacity and direction - float opacity = (-1 * lPos.x) * 0.5; - - if (Rotational && (Vertical == false)) - { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; - } - - if (Vertical && (Rotational == false)) - { - opacity = (-1 * lPos.y) * 0.5; - } + float2 uv = v_in.uv; + uv.y = 1.0 - uv.y; + + // calculate the intensity bucket for this pixel based on column height (padded at the top) + const float max_value = 270.0; + const float buckets = 512.0; + float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); + float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); + + // count the count the r,g,b and luma in this column that match the bucket + float4 count = float4(0.0, 0.0, 0.0, 0.0); + for( int i=0; i < 512; ++i ) { + float j = float(i) / buckets; + float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; + + // calculate the Rec.709 luma for this pixel + pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; - opacity += time; - opacity = frac(opacity); - point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); - } - return point_color; + float4 logpixel = log(pixel); + if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; + if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; + if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; + if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; + } + + // sum luma into RGB, tweak log intensity for readability + count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; + + // output + return count; } - - - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -21707,14 +27189,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlinkShader { +function Get-OBSInvertLumaShader { -[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] +[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] param( -# Set the speed of OBSBlinkShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, +# Set the Invert_Color of OBSInvertLumaShader +[Alias('Invert_Color')] +[ComponentModel.DefaultBindingProperty('Invert_Color')] +[Management.Automation.SwitchParameter] +$InvertColor, +# Set the Invert_Luma of OBSInvertLumaShader +[Alias('Invert_Luma')] +[ComponentModel.DefaultBindingProperty('Invert_Luma')] +[Management.Automation.SwitchParameter] +$InvertLuma, +# Set the Gamma_Correction of OBSInvertLumaShader +[Alias('Gamma_Correction')] +[ComponentModel.DefaultBindingProperty('Gamma_Correction')] +[Management.Automation.SwitchParameter] +$GammaCorrection, +# Set the Test_Ramp of OBSInvertLumaShader +[Alias('Test_Ramp')] +[ComponentModel.DefaultBindingProperty('Test_Ramp')] +[Management.Automation.SwitchParameter] +$TestRamp, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -21745,23 +27243,121 @@ $UseShaderTime process { -$shaderName = 'blink' -$ShaderNoun = 'OBSBlinkShader' +$shaderName = 'invert-luma' +$ShaderNoun = 'OBSInvertLumaShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; +// Invert shader 1.0 - for OBS Shaderfilter +// Copyright 2021 by SkeletonBow +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv -float4 mainImage(VertData v_in) : TARGET +// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling + +uniform bool Invert_Color = false; +uniform bool Invert_Luma = true; +uniform bool Gamma_Correction = true; +uniform bool Test_Ramp = false; + +float3 encodeSRGB(float3 linearRGB) { - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - return float4(color.r, color.g, color.b, color.a * (1 + sin(t)) / 2); + float3 a = float3(12.92,12.92,12.92) * linearRGB; + float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); + float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); + return float3(lerp(a, b, c)); +} + +float3 decodeSRGB(float3 screenRGB) +{ + float3 a = screenRGB / float3(12.92,12.92,12.92); + float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); + float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); + return float3(lerp(a, b, c)); +} + +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); +} + +float3 RGBtoYUV(float3 color) +{ + // YUV matriz (BT709 luma coefficients) +#ifdef OPENGL + float3x3 toYUV = float3x3( + float3(0.2126, -0.09991, 0.615), + float3(0.7152, -0.33609, -0.55861), + float3(0.0722, 0.436, -0.05639)); +#else + float3x3 toYUV = { + { 0.2126, -0.09991, 0.615 }, + { 0.7152, -0.33609, -0.55861 }, + { 0.0722, 0.436, -0.05639 }, + }; +#endif + return mul(color, toYUV); +} + +float3 YUVtoRGB(float3 color) +{ + // YUV matriz (BT709) +#ifdef OPENGL + float3x3 fromYUV = float3x3( + float3(1.000, 1.000, 1.000), + float3(0.0, -0.21482, 2.12798), + float3(1.28033, -0.38059, 0.0)); +#else + float3x3 fromYUV = { + { 1.000, 1.000, 1.000 }, + { 0.0, -0.21482, 2.12798 }, + { 1.28033, -0.38059, 0.0 }, + }; +#endif + return mul(color, fromYUV); +} + +float3 generate_ramps(float3 color, float2 uv) +{ + float3 ramp = float3(0.0, 0.0, 0.0); + if(uv.y < 0.2) + ramp.r = uv.x; // Red ramp + else if(uv.y < 0.4) + ramp.g = uv.x; // Green ramp + else if(uv.y < 0.6) + ramp.b = uv.x; // Blue ramp + else if(uv.y < 0.8) + ramp = float3(uv.x, uv.x, uv.x); // Grey ramp + else + ramp = HUEtoRGB(uv.x); // Hue rainbow + + return ramp; +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float4 obstex = image.Sample( textureSampler, uv ); + float3 color = obstex.rgb; + // Apply sRGB gamma transfer encode + if(Gamma_Correction) color = encodeSRGB( color ); + // Override display with test patterns to visually see what is happening + if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); + // RGB color invert + if( Invert_Color ) { + color = float3(1.0, 1.0, 1.0) - color; + } + // YUV luma invert + if( Invert_Luma ) { + float3 yuv = RGBtoYUV( color ); + yuv.x = 1.0 - yuv.x; + color = YUVtoRGB(yuv); + } + // Apply sRGB gamma transfer decode + if(Gamma_Correction) color = decodeSRGB( color ); + return float4(color, obstex.a); } ' @@ -21861,25 +27457,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBloomShader { +function Get-OBSLuminance2Shader { -[Alias('Set-OBSBloomShader','Add-OBSBloomShader')] +[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] param( -# Set the Angle_Steps of OBSBloomShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSBloomShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSBloomShader -[ComponentModel.DefaultBindingProperty('ampFactor')] +# Set the color of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the lumaMax of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMax')] [Single] -$AmpFactor, -# Set the notes of OBSBloomShader +$LumaMax, +# Set the lumaMin of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMaxSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] +[Single] +$LumaMaxSmooth, +# Set the lumaMinSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the invertImageColor of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminance2Shader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -21913,75 +27523,83 @@ $UseShaderTime process { -$shaderName = 'bloom' -$ShaderNoun = 'OBSBloomShader' +$shaderName = 'luminance2' +$ShaderNoun = 'OBSLuminance2Shader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Bloom shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 color; +uniform float lumaMax< + string label = "Luma Max"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; // -uniform int Radius_Steps< - string label = "Radius Steps"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.05; +uniform float lumaMin< + string label = "Luma Min"; string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; // -uniform float ampFactor< - string label = "amp Factor"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMaxSmooth< + string label = "Luma Max Smooth"; string widget_type = "slider"; float minimum = 0.0; float maximum = 10.0; - float step = 0.01; -> = 2.0; + float step = 0.001; +> = 0.10; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; uniform string notes< string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; +> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; + +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} float4 mainImage(VertData v_in) : TARGET { - int radiusSteps = clamp(Radius_Steps, 0, 20); - int angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - float minRadius = (0.0 * uv_pixel_interval.y); - float maxRadius = (10.0 * uv_pixel_interval.y); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 outputPixel = c0; - float4 accumulatedColor = float4(0,0,0,0); - - int totalSteps = radiusSteps * angleSteps; - float angleDelta = (2.0 * PI) / float(angleSteps); - float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); - - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + float(radiusStep) * radiusDelta; - - for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { - float2 currentCoord; - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor =image.Sample(textureSampler, currentCoord); - float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); + } + float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - accumulatedColor += currentFraction * currentColor / float(totalSteps); - - } - } + //intensity = min(max(intensity,minIntensity),maxIntensity); + float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); + float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); - outputPixel += accumulatedColor * ampFactor; + float amask = clo * chi; - return outputPixel; + if (invertAlphaChannel) + { + amask = 1.0 - amask; + } + rgba *= color; + rgba.a = clamp(amask, 0.0, 1.0); + + } + return rgba; } ' @@ -22081,14 +27699,75 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBorderShader { +function Get-OBSLuminanceAlphaShader { -[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] +[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] param( -# Set the borderColor of OBSBorderShader -[ComponentModel.DefaultBindingProperty('borderColor')] +# Set the ViewProj of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$BorderColor, +$Image, +# Set the elapsed_time of OBSLuminanceAlphaShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSLuminanceAlphaShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSLuminanceAlphaShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSLuminanceAlphaShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSLuminanceAlphaShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSLuminanceAlphaShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the color_matrix of OBSLuminanceAlphaShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the mul_val of OBSLuminanceAlphaShader +[Alias('mul_val')] +[ComponentModel.DefaultBindingProperty('mul_val')] +[Single] +$MulVal, +# Set the add_val of OBSLuminanceAlphaShader +[Alias('add_val')] +[ComponentModel.DefaultBindingProperty('add_val')] +[Single] +$AddVal, +# Set the level of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('level')] +[Single] +$Level, +# Set the invertAlphaChannel of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -22119,24 +27798,110 @@ $UseShaderTime process { -$shaderName = 'border' -$ShaderNoun = 'OBSBorderShader' +$shaderName = 'luminance_alpha' +$ShaderNoun = 'OBSLuminanceAlphaShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 borderColor; +// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; -float4 mainImage(VertData v_in) : TARGET +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; + +uniform float4x4 color_matrix; +uniform float4 color; +uniform float mul_val< + string label = "Mulitply"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float add_val< + string label = "Add"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 0.0; +uniform float level< + string label = "Level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> =1.0; +uniform bool invertAlphaChannel; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; + +VertDataOut mainTransform(VertDataIn v_in) { - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); + vert_out.uv = v_in.uv * mul_val + add_val; + vert_out.uv2 = v_in.uv ; + return vert_out; +} + +/*float3 GetLuminance(float4 rgba) +{ + float red = rbga.r; + float green = rgba.g; + float blue = rgba.b; + return (.299 * red) + (.587 * green) + (.114 * blue); +}*/ + +float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv) ; + if (rgba.a > 0.0) { - return borderColor; - } - else + + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + if (invertAlphaChannel) { - return image.Sample(textureSampler, v_in.uv); + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + } + return rgba; } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = PSAlphaMaskRGBA(v_in); + } +} + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -22234,34 +27999,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBoxBlurShader { +function Get-OBSLuminanceShader { -[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] +[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] param( -# Set the Strength of OBSBoxBlurShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSBoxBlurShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSBoxBlurShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] +# Set the color of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the level of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('level')] [Single] -$MaskRight, -# Set the Mask_Top of OBSBoxBlurShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSBoxBlurShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, +$Level, +# Set the invertImageColor of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -22292,90 +28053,62 @@ $UseShaderTime process { -$shaderName = 'box-blur' -$ShaderNoun = 'OBSBoxBlurShader' +$shaderName = 'Luminance' +$ShaderNoun = 'OBSLuminanceShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; +//Converted to OpenGL by Exeldro February 22, 2022 +uniform float4 color; +uniform float level< + string label = "Level"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; + float maximum = 10.0; float step = 0.01; > = 1.0; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; + +uniform string notes< + string widget_type = "info"; +> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; + +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} float4 mainImage(VertData v_in) : TARGET { - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - float transparent = 0.0; - int count = 1; - float samples = 0.0; - float4 c = float4(0.0, 0.0, 0.0, 0.0); - float Steps = float(Strength); - - [loop] for (int i = -Strength; i <= Strength; i++) { - [loop] for (int k = -Strength; k <= Strength; k++) { - float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); - transparent += sc.a; - count++; - c += sc * sc.a; - samples += sc.a; - } + + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); } - if(samples > 0.0) - c /= samples; + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - c.a = transparent / float(count); - return c; + //intensity = min(max(intensity,minIntensity),maxIntensity); + + + if (invertAlphaChannel) + { + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + + } + return rgba; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -22473,36 +28206,98 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBulgePinchShader { +function Get-OBSMatrixShader { -[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] +[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] param( -# Set the radius of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('radius')] +# Set the ViewProj of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSMatrixShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$Radius, -# Set the magnitude of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('magnitude')] +$ElapsedTime, +# Set the uv_offset of OBSMatrixShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSMatrixShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSMatrixShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSMatrixShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSMatrixShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$Magnitude, -# Set the center_x of OBSBulgePinchShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] +$RandF, +# Set the rand_instance_f of OBSMatrixShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] [Single] -$CenterX, -# Set the center_y of OBSBulgePinchShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] +$RandInstanceF, +# Set the rand_activation_f of OBSMatrixShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] [Single] -$CenterY, -# Set the animate of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('animate')] +$RandActivationF, +# Set the loops of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSMatrixShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the mouse of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('mouse')] +[Single[]] +$Mouse, +# Set the Invert_Direction of OBSMatrixShader +[Alias('Invert_Direction')] +[ComponentModel.DefaultBindingProperty('Invert_Direction')] [Management.Automation.SwitchParameter] -$Animate, -# Set the notes of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$InvertDirection, +# Set the lumaMin of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Ratio of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('Ratio')] +[Single] +$Ratio, +# Set the Alpha_Percentage of OBSMatrixShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSMatrixShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -22533,78 +28328,228 @@ $UseShaderTime process { -$shaderName = 'BulgePinch' -$ShaderNoun = 'OBSBulgePinchShader' +$shaderName = 'matrix' +$ShaderNoun = 'OBSMatrixShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "Radius"; +// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated + +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float + +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; + + +uniform float2 mouse< + string label = "Virtual Mouse Coordinates"; + string widget_type = "slider"; + float2 minimum = {0, 0}; + float2 maximum = {100., 100.}; + float2 scale = {.01, .01}; + float2 step = {.01, .01}; +> = {0., 0.}; + + +int2 iMouse() { + return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); +} + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +/* ps start + +*/ + +uniform bool Invert_Direction< + string label = "Invert Direction"; +> = true; + +uniform float lumaMin< + string label = "Luma Min"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.0; -uniform float magnitude< - string label = "Magnitude"; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; string widget_type = "slider"; - float minimum = -1.3333; - float maximum = 1.3333; - float step = 0.01; -> = 0.0; -uniform float center_x< - string label = "Center x"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float Ratio< + string label = "Ratio"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 0.5; + float maximum = 100.0; float step = 0.01; -> = 0.25; -uniform float center_y< - string label = "Center y"; +> = 4.0; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 0.5; + float maximum = 100.0; float step = 0.01; -> = 0.25; -uniform bool animate = false; +> = 100; // +uniform bool Apply_To_Alpha_Layer = true; -uniform string notes< - string widget_type = "info"; -> = "Distorts the screen, expanding or drawing in pixels around a point." +#define PI2 6.28318530718 +#define PI 3.1416 -float4 mainImage(VertData v_in) : TARGET +float vorocloud(float2 p){ + float f = 0.0; + float flow = 1.0; + float time = elapsed_time; + if(Invert_Direction){ + flow *= -1; + } + /* + //periodically stop + if (loops % 16 >= 8.0) + { + time = local_time - elapsed_time; + } + */ + + float r = clamp(Ratio,-50,50); + float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); + p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); + + float2 pts[4]; + + pts[0] = float2(0.5, 0.6); + pts[1] = float2(-0.4, 0.4); + pts[2] = float2(0.2, -0.7); + pts[3] = float2(-0.3, -0.4); + + float d = 5.0; + + for(int i = 0; i < 4; i++){ + pts[i].x += 0.03 * cos(float(i)) + p.x; + pts[i].y += 0.03 * sin(float(i)) + p.y; + d = min(d, distance(pts[i], pp)); + } + + f = 2.0 * pow(1.0 - 0.3 * d, 13.0); + + f = min(f, 1.0); + + return f; +} + +vec4 scene(float2 UV){ + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); + + float x = UV.x; + float y = UV.y; + + float2 p = float2(x, y) - 0.5; + + vec4 col = vec4(0.0,0.0,0.0,0.0); + col.g += 0.02; + + float v = vorocloud(p); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.6 * v; + col.b += 0.5 * pow(v, 5.0); + + + v = vorocloud(p * 2.0); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.2 * v; + col.b += 0.01 * pow(v, 5.0); + + col.a = 1.0; + float luma = dot(col.rgb,float3(0.299,0.587,0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min,0.0,1.0); + + float4 original_color = image.Sample(textureSampler, UV); + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + if (Apply_To_Alpha_Layer == false) + original_color.a = alpha; + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } + else + { + col.a = original_color.a; + } + + return col; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - v_out.uv = 1. * v_in.uv - center; + vec2 uv = fragCoord.xy / uv_size; + fragColor = scene(uv); +} - center.x /= ar; - v_out.uv.x /= ar; +/*ps end*/ - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); - float percent = dist/radius; - if(anim_mag > 0) - v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); - else - v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - v_out.uv += (2 * center); - v_out.uv.x *= ar; +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv); - } +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -22702,61 +28647,19 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBurnShader { +function Get-OBSMotionBlurShader { -[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] +[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] param( -# Set the Burn_Gradient of OBSBurnShader -[Alias('Burn_Gradient')] -[ComponentModel.DefaultBindingProperty('Burn_Gradient')] +# Set the previous_output of OBSMotionBlurShader +[Alias('previous_output')] +[ComponentModel.DefaultBindingProperty('previous_output')] [String] -$BurnGradient, -# Set the Speed of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Gradient_Adjust of OBSBurnShader -[Alias('Gradient_Adjust')] -[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] -[Single] -$GradientAdjust, -# Set the Dissolve_Value of OBSBurnShader -[Alias('Dissolve_Value')] -[ComponentModel.DefaultBindingProperty('Dissolve_Value')] -[Single] -$DissolveValue, -# Set the Animated of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the Apply_to_Channel of OBSBurnShader -[Alias('Apply_to_Channel')] -[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the Apply_Smoke of OBSBurnShader -[Alias('Apply_Smoke')] -[ComponentModel.DefaultBindingProperty('Apply_Smoke')] -[Management.Automation.SwitchParameter] -$ApplySmoke, -# Set the Smoke_Horizonal_Speed of OBSBurnShader -[Alias('Smoke_Horizonal_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] -[Single] -$SmokeHorizonalSpeed, -# Set the Smoke_Vertical_Speed of OBSBurnShader -[Alias('Smoke_Vertical_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] +$PreviousOutput, +# Set the strength of OBSMotionBlurShader +[ComponentModel.DefaultBindingProperty('strength')] [Single] -$SmokeVerticalSpeed, -# Set the Iterations of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Iterations')] -[Int32] -$Iterations, -# Set the Notes of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, +$Strength, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -22787,168 +28690,22 @@ $UseShaderTime process { -$shaderName = 'burn' -$ShaderNoun = 'OBSBurnShader' +$shaderName = 'motion_blur' +$ShaderNoun = 'OBSMotionBlurShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Exeldro February 17, 2022 -float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} -float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} -float4 permute(float4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} -float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} -float2 fade(float2 t) { - return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); -} - -float dot(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} - -// Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} -// Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - -uniform texture2d Burn_Gradient = "burngradient.png"; -uniform float Speed = 0.33; -uniform float Gradient_Adjust = 0.85; -uniform float Dissolve_Value = 1.43; -uniform bool Animated; -uniform bool Apply_to_Channel; -uniform bool Apply_Smoke = true; -uniform float Smoke_Horizonal_Speed = 0.3; -uniform float Smoke_Vertical_Speed = 0.17; -uniform int Iterations = 4; -uniform string Notes< - string widget_type = "info"; -> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; +uniform texture2d previous_output; +uniform float strength< + string label = "strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - float4 smoke = float4(1.0,1.0,1.0,1.0); - float4 result = smoke; - float t = elapsed_time * Speed; - float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay - float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); - //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); - - float perlin = 0.5; - float smoke_perlin = 0; - float scale = 1; - float w = 0.5; - - for (int i = 0; i < Iterations; i++) { - //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; - float2 coord = (v_in.uv + t * (dir * .1)) * scale; - float2 period = scale * dir; - perlin += pnoise(coord, period) * w; - if (Apply_Smoke) - smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; - - scale *= 2.0; - w *= 0.5; - } - - //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); - if (!Animated) - cycle = 1; - float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); - float overOne = saturate(d * Gradient_Adjust); - float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); - - if (Apply_to_Channel) { - result = c * burn; - } - else { - result = float4(perlin, perlin, perlin, 1.0) * burn; - } - - if (smoke_perlin > 0) { - smoke *= smoke_perlin; - if (result.a <= 0.04) - result = float4(smoke.rgb, smoke_perlin); - result += smoke; - } - - return result; + return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); } ' } @@ -23047,67 +28804,15 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCartoonShader { +function Get-OBSMultiplyShader { -[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] +[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] param( -# Set the ViewProj of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSCartoonShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSCartoonShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSCartoonShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSCartoonShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSCartoonShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSCartoonShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the other_image of OBSMultiplyShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] [String] -$Notes, -# Set the hue_steps of OBSCartoonShader -[Alias('hue_steps')] -[ComponentModel.DefaultBindingProperty('hue_steps')] -[Int32] -$HueSteps, -# Set the value_steps of OBSCartoonShader -[Alias('value_steps')] -[ComponentModel.DefaultBindingProperty('value_steps')] -[Int32] -$ValueSteps, -# Set the Apply_To_Alpha_Layer of OBSCartoonShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, +$OtherImage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -23138,104 +28843,17 @@ $UseShaderTime process { -$shaderName = 'cartoon' -$ShaderNoun = 'OBSCartoonShader' +$shaderName = 'multiply' +$ShaderNoun = 'OBSMultiplyShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "5/2 seems reasonable"; - -uniform int hue_steps = 5; -uniform int value_steps = 2; -uniform bool Apply_To_Alpha_Layer = true; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertDataOut VSDefault(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv; - return vert_out; -} - -float3 rgb2hsv(float3 c) -{ - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -float3 hsv2rgb(float3 c) -{ - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} - -float fit(float v, int factor) -{ - return round(v * factor) / factor; -} - -float hue_wrap(float h) -{ - return fmod(h + 1, 2) - 1; - if(h > 1) - return h - 2; - if(h < -1) - return h + 2; - return h; -} - -float4 PassThrough(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) - { - float3 hsv = rgb2hsv(rgba.rgb); - hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); - //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); - rgba = float4(hsv2rgb(hsv), rgba.a); - //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); - } - return rgba; -} +uniform texture2d other_image; -technique Draw +float4 mainImage(VertData v_in) : TARGET { - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PassThrough(v_in); - } + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return base * other; } ' @@ -23335,28 +28953,99 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCellShadedShader { +function Get-OBSNightSkyShader { -[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] +[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] param( -# Set the Angle_Steps of OBSCellShadedShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] +# Set the speed of OBSNightSkyShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the Include_Clouds of OBSNightSkyShader +[Alias('Include_Clouds')] +[ComponentModel.DefaultBindingProperty('Include_Clouds')] +[Management.Automation.SwitchParameter] +$IncludeClouds, +# Set the Include_Moon of OBSNightSkyShader +[Alias('Include_Moon')] +[ComponentModel.DefaultBindingProperty('Include_Moon')] +[Management.Automation.SwitchParameter] +$IncludeMoon, +# Set the center_width_percentage of OBSNightSkyShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] [Int32] -$AngleSteps, -# Set the Radius_Steps of OBSCellShadedShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSNightSkyShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] [Int32] -$RadiusSteps, -# Set the ampFactor of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('ampFactor')] +$CenterHeightPercentage, +# Set the Alpha_Percentage of OBSNightSkyShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] [Single] -$AmpFactor, -# Set the notes of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('notes')] +$AlphaPercentage, +# Set the Apply_To_Image of OBSNightSkyShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSNightSkyShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the number_stars of OBSNightSkyShader +[Alias('number_stars')] +[ComponentModel.DefaultBindingProperty('number_stars')] +[Int32] +$NumberStars, +# Set the SKY_COLOR of OBSNightSkyShader +[Alias('SKY_COLOR')] +[ComponentModel.DefaultBindingProperty('SKY_COLOR')] [String] -$Notes, +$SKYCOLOR, +# Set the STAR_COLOR of OBSNightSkyShader +[Alias('STAR_COLOR')] +[ComponentModel.DefaultBindingProperty('STAR_COLOR')] +[String] +$STARCOLOR, +# Set the LIGHT_SKY of OBSNightSkyShader +[Alias('LIGHT_SKY')] +[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] +[String] +$LIGHTSKY, +# Set the SKY_LIGHTNESS of OBSNightSkyShader +[Alias('SKY_LIGHTNESS')] +[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] +[Single] +$SKYLIGHTNESS, +# Set the MOON_COLOR of OBSNightSkyShader +[Alias('MOON_COLOR')] +[ComponentModel.DefaultBindingProperty('MOON_COLOR')] +[String] +$MOONCOLOR, +# Set the moon_size of OBSNightSkyShader +[Alias('moon_size')] +[ComponentModel.DefaultBindingProperty('moon_size')] +[Single] +$MoonSize, +# Set the moon_bump_size of OBSNightSkyShader +[Alias('moon_bump_size')] +[ComponentModel.DefaultBindingProperty('moon_bump_size')] +[Single] +$MoonBumpSize, +# Set the Moon_Position_x of OBSNightSkyShader +[Alias('Moon_Position_x')] +[ComponentModel.DefaultBindingProperty('Moon_Position_x')] +[Single] +$MoonPositionX, +# Set the Moon_Position_y of OBSNightSkyShader +[Alias('Moon_Position_y')] +[ComponentModel.DefaultBindingProperty('Moon_Position_y')] +[Single] +$MoonPositionY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -23387,74 +29076,297 @@ $UseShaderTime process { -$shaderName = 'cell_shaded' -$ShaderNoun = 'OBSCellShadedShader' +$shaderName = 'night_sky' +$ShaderNoun = 'OBSNightSkyShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; -uniform int Radius_Steps< - string label = "Radius Steps"; +// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 +// https://github.com/Oncorporation/obs-shaderfilter +//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float speed< + string label = "Speed"; string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; -uniform float ampFactor< - string label = "amp Factor"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 20.0; +uniform bool Include_Clouds = true; +uniform bool Include_Moon = true; +uniform int center_width_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; float step = 0.01; -> = 2.0; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; +> = 95.0; // +uniform bool Apply_To_Image = false; +uniform bool Replace_Image_Color = false; +uniform int number_stars< + string label = "Number stars"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; // + +uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; +uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; +uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; +uniform float SKY_LIGHTNESS< + string label = "SKY LIGHTNESS"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = .3; + + // Moon +uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; +uniform float moon_size< + string label = "Moon size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.18; +uniform float moon_bump_size< + string label = "Moon bump size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.14; +uniform float Moon_Position_x< + string label = "Moon Position x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.6; +uniform float Moon_Position_y< + string label = "Moon Position y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.3; + +#define PI 3.1416 + +//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU +float noise11(float p) { + return frac(sin(p*633.1847) * 9827.95); +} + +float noise21(float2 p) { + return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); +} + +float2 noise22(float2 p) { + return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); +} + +//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise +float cosineInterpolation(float a, float b, float x) { + float ft = x * PI; + float f = (1. - cos(ft)) * .5; + return a * (1. - f) + b * f; +} + +float smoothNoise11(float p, float dist) { + float prev = noise11(p-dist); + float next = noise11(p+dist); + + return cosineInterpolation(prev, next, .5); +} + +float smoothNoise21(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); + + //smoothstep function: maybe change it later! + lv = lv*lv*(3.-2.*lv); + + float bl = noise21(id); + float br = noise21(id+float2(1.,0.)); + float b = lerp(bl, br, lv.x); + + float tl = noise21(id+float2(0.,1.)); + float tr = noise21(id+float2(1.,1.)); + float t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); +} + +float2 smoothNoise22(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); + + lv = lv*lv*(3.-2.*lv); + + float2 bl = noise22(id); + float2 br = noise22(id+float2(1.,0.)); + float2 b = lerp(bl, br, lv.x); + + float2 tl = noise22(id+float2(0.,1.)); + float2 tr = noise22(id+float2(1.,1.)); + float2 t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); +} + +float valueNoise11(float p) { + float c = smoothNoise11(p, 0.5); + c += smoothNoise11(p, 0.25)*.5; + c += smoothNoise11(p, 0.125)*.25; + c += smoothNoise11(p, 0.0625)*.125; + + return c /= .875; +} + +float valueNoise21(float2 uv) { + float c = smoothNoise21(uv, 4.); + c += smoothNoise21(uv, 8.)*.5; + c += smoothNoise21(uv, 16.)*.25; + c += smoothNoise21(uv, 32.)*.125; + c += smoothNoise21(uv, 64.)*.0625; + + return c /= .9375; +} + +float2 valueNoise22(float2 uv) { + float2 c = smoothNoise22(uv, 4.); + c += smoothNoise22(uv, 8.)*.5; + c += smoothNoise22(uv, 16.)*.25; + c += smoothNoise22(uv, 32.)*.125; + c += smoothNoise22(uv, 64.)*.0625; + + return c /= .9375; +} + +float3 points(float2 p, float2 uv, float3 color, float size, float blur) { + float dist = distance(p, uv); + return color*smoothstep(size, size*(0.999-blur), dist); +} + +float mapInterval(float x, float a, float b, float c, float d) { + return (x-a)/(b-a) * (d-c) + c; +} + +float blink(float time, float timeInterval) { + float halfInterval = timeInterval / 2.0; + //Get relative position in the bucket + float p = fmod(time, timeInterval); + + + if (p <= timeInterval / 2.) { + return smoothstep(0., 1., p/halfInterval); + } else { + return smoothstep(1., 0., (p-halfInterval)/halfInterval); + } +} + +float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { + float dist = distance(p, uv); + float3 BumpSamples = float3(0.,0.,0.); + + if (dist < radius) { + float bumps = (1.-valueNoise21(uv*spin))*.1; + BumpSamples = float3(bumps, bumps, bumps); + } + return BumpSamples; +} float4 mainImage(VertData v_in) : TARGET { - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (3 * uv_pixel_interval.y); - float maxRadius = (24 * uv_pixel_interval.y); + float4 rgba;// = image.Sample(textureSampler, v_in.uv); + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st; - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); + // Normalized pixel coordinates (from 0 to 1) + float2 uv = v_in.uv; + float2 ouv = uv; + uv -= .5; + uv.x *= uv_size.x/uv_size.y; + + float2 seed = toCenter / uv_size.xy; + + float time = elapsed_time + seed.x*speed; + + //float3 col = float3(0.0); + //float m = valueNoise21(uv); + float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); + + col *= SKY_LIGHTNESS - (1.-ouv.y); + + //Add clouds + if (Include_Clouds) + { + float2 timeUv = uv; + timeUv.x += time*.1; + timeUv.y += valueNoise11(timeUv.x+.352)*.01; + float cloud = valueNoise21(timeUv); + col += cloud*.1; + } - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); + //Add stars in the top part of the scene + float timeInterval = speed *.5; //5.0 + float timeBucket = floor(time / timeInterval); - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; + float2 moonPosition = float2(-1000, -1000); + if (Include_Moon) + { + moonPosition = float2(Moon_Position_x, Moon_Position_y); + col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); + // Moon bumps + col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); + } - for (float angle=0; angle <(2*PI); angle += angleDelta) { - float2 currentCoord; + for (float i = 0.; i < clamp(number_stars,0,100); i++) { + float2 starPosition = float2(i/10., i/10.); + + starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); + starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); + + float starIntensity = blink(time+ (rand_f * i), timeInterval ); + //Hide stars that are behind the moon + if (distance(starPosition, moonPosition) > moon_size) { + col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; + col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; + } + } + //col = float3(blink(time, timeInterval)); + rgba = float4(col,alpha); - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps; - - } + // Output to screen + if (Apply_To_Image) + { + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color,alpha); + } - accumulatedColor *= ampFactor; - - return c0 - accumulatedColor; // Cell shaded style + return rgba; } ' @@ -23554,48 +29466,31 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaticAberrationShader { +function Get-OBSNoiseShader { -[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] +[Alias('Set-OBSNoiseShader','Add-OBSNoiseShader')] param( -# Set the power of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('power')] +# Set the speed of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] [Single] -$Power, -# Set the gamma of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('gamma')] +$Speed, +# Set the scale of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('scale')] [Single] -$Gamma, -# Set the num_iter of OBSChromaticAberrationShader -[Alias('num_iter')] -[ComponentModel.DefaultBindingProperty('num_iter')] -[Int32] -$NumIter, -# Set the distort_radial of OBSChromaticAberrationShader -[Alias('distort_radial')] -[ComponentModel.DefaultBindingProperty('distort_radial')] -[Management.Automation.SwitchParameter] -$DistortRadial, -# Set the distort_barrel of OBSChromaticAberrationShader -[Alias('distort_barrel')] -[ComponentModel.DefaultBindingProperty('distort_barrel')] -[Management.Automation.SwitchParameter] -$DistortBarrel, -# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader -[Alias('offset_spectrum_ycgco')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] -[Management.Automation.SwitchParameter] -$OffsetSpectrumYcgco, -# Set the offset_spectrum_yuv of OBSChromaticAberrationShader -[Alias('offset_spectrum_yuv')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] +$Scale, +# Set the noiseLevel of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('noiseLevel')] +[Single] +$NoiseLevel, +# Set the monochromatic of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('monochromatic')] [Management.Automation.SwitchParameter] -$OffsetSpectrumYuv, -# Set the use_random of OBSChromaticAberrationShader -[Alias('use_random')] -[ComponentModel.DefaultBindingProperty('use_random')] +$Monochromatic, +# Set the use_rand of OBSNoiseShader +[Alias('use_rand')] +[ComponentModel.DefaultBindingProperty('use_rand')] [Management.Automation.SwitchParameter] -$UseRandom, +$UseRand, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -23626,215 +29521,322 @@ $UseShaderTime process { -$shaderName = 'chromatic-aberration' -$ShaderNoun = 'OBSChromaticAberrationShader' +$shaderName = 'noise' +$ShaderNoun = 'OBSNoiseShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/XssGz8 -//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 -uniform float power< - string label = "Power"; +uniform float speed< + string label = "Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.01; -uniform float gamma< - string label = "Gamma"; + float minimum = 0; + float maximum = 100; + float step = 0.1; +> = 1; +uniform float scale< + string label = "Scale"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3.0; - float step = 0.01; -> = 2.2; -uniform int num_iter< - string label = "Number iterations"; + float minimum = 0; + float maximum = 10; + float step = 0.0001; +> = 6; +uniform float noiseLevel< + string label = "Noise Level"; string widget_type = "slider"; - int minimum = 3; - int maximum = 25; - int step = 1; -> = 7; -uniform bool distort_radial = false; -uniform bool distort_barrel = false; -uniform bool offset_spectrum_ycgco = false; -uniform bool offset_spectrum_yuv = false; -uniform bool use_random = true; - -float2 remap( float2 t, float2 a, float2 b ) { - return clamp( (t - a) / (b - a), 0.0, 1.0 ); -} + float minimum = 0; + float maximum = 1; + float step = 0.001; +> = 1; +uniform bool monochromatic = false; +uniform bool use_rand = false; -float3 spectrum_offset_rgb( float t ) +float rand(float2 st) { - float t0 = 3.0 * t - 1.5; - float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); - return ret; + return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123); } +float4 mainImage(VertData v_in) : TARGET +{ + float time = rand_activation_f + (speed / 60) * elapsed_time * 0.00001; -float3 lin2srgb( float3 c ) -{ - return pow( c, float3(gamma, gamma, gamma) ); -} -float3 srgb2lin( float3 c ) -{ - return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); -} + if (use_rand) { + time = rand_f; + } -float3 yCgCo2rgb(float3 ycc) -{ - float R = ycc.x - ycc.y + ycc.z; - float G = ycc.x + ycc.y; - float B = ycc.x - ycc.y - ycc.z; - return float3(R,G,B); -} + float4 x; -float3 spectrum_offset_ycgco( float t ) -{ - //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink - //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple - float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange - //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue - return yCgCo2rgb( ygo ); -} + if (monochromatic) { + x = rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + scale); + } else { + x = float4( + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 1 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 2 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 3 * scale), + 1 + ); + } -float3 yuv2rgb( float3 yuv ) -{ - float3 rgb; - rgb.r = yuv.x + yuv.z * 1.13983; - rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); - rgb.b = yuv.x + yuv.y * 2.03211; - return rgb; -} + float4 rgba = image.Sample(textureSampler, v_in.uv); -float2 radialdistort(float2 coord, float2 amt) -{ - float2 cc = coord - 0.5; - return coord + 2.0 * cc * amt; + float3 output = lerp(rgba, x, noiseLevel); + + return float4(output.r, output.g, output.b, rgba.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' } +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } -float2 barrelDistortion( float2 p, float2 amt ) -{ - p = 2.0 * p - 1.0; + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } - /* - const float maxBarrelPower = 5.0; - //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper - float theta = atan(p.y, p.x); - float2 radius = float2( length(p) ); - radius = pow(radius, 1.0 + maxBarrelPower * amt); - p.x = radius.x * cos(theta); - p.y = radius.y * sin(theta); + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - /*/ - // much faster version - //const float maxBarrelPower = 5.0; - //float radius = length(p); - float maxBarrelPower = sqrt(5.0); - float radius = dot(p,p); //faster but doesn''t match above accurately - p *= pow(float2(radius, radius), maxBarrelPower * amt); - /* */ + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } - return p * 0.5 + 0.5; -} + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } -float2 brownConradyDistortion(float2 uv, float dist) -{ - uv = uv * 2.0 - 1.0; - // positive values of K1 give barrel distortion, negative give pincushion - float barrelDistortion1 = 0.1 * dist; // K1 in text books - float barrelDistortion2 = -0.025 * dist; // K2 in text books + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - float r2 = dot(uv,uv); - uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; - //uv *= 1.0 + barrelDistortion1 * r2; - - // tangential distortion (due to off center lens elements) - // is not modeled in this function, but if it was, the terms would go here - return uv * 0.5 + 0.5; + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } } -float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) -{ - float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); - //float2 dist = mix( min_distort, max_distort, t ); - if (distort_radial) - return radialdistort( uv, 2.0 * dist ); - - if(distort_barrel) - return barrelDistortion( uv, 1.75 * dist ); //distortion at center - return brownConradyDistortion( uv, 75.0 * dist.x ); } -// ==== -float3 spectrum_offset_yuv( float t ) -{ - //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green - //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green - float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange - //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue - return yuv2rgb( yuv ); -} +} -float3 spectrum_offset( float t ) -{ - if(offset_spectrum_ycgco) - return spectrum_offset_ycgco( t ); - if(offset_spectrum_yuv) - return spectrum_offset_yuv( t ); - return spectrum_offset_rgb( t ); - //return srgb2lin( spectrum_offset_rgb( t ) ); - //return lin2srgb( spectrum_offset_rgb( t ) ); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSNormalMapShader { -float4 mainImage(VertData v_in) : TARGET -{ - float2 max_distort = float2(power, power); - float2 min_distort = 0.5 * max_distort; +[Alias('Set-OBSNormalMapShader','Add-OBSNormalMapShader')] +param( +# Set the strength of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the offsetHeight of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('offsetHeight')] +[Management.Automation.SwitchParameter] +$OffsetHeight, +# Set the invertR of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertR')] +[Management.Automation.SwitchParameter] +$InvertR, +# Set the invertG of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertG')] +[Management.Automation.SwitchParameter] +$InvertG, +# Set the invertH of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertH')] +[Management.Automation.SwitchParameter] +$InvertH, +# Set the type of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('type')] +[Int32] +$Type, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); - float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); - - //debug oversiz - //float2 distuv = distort( uv, 1.0, max_distort ); - //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) - //{ - // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; - //} - - - float stepsiz = 1.0 / (float(num_iter)-1.0); - float rnd = 0.0; - if(use_random) - rnd = rand_f; - - float t = rnd * stepsiz; +process { +$shaderName = 'normal_map' +$ShaderNoun = 'OBSNormalMapShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Normal map shader based on cpetry''s NormalMap-Online website +// https://github.com/cpetry/NormalMap-Online/blob/gh-pages/javascripts/shader/NormalMapShader.js - float3 sumcol = float3(0.0, 0.0, 0.0); - float3 sumw = float3(0.0, 0.0, 0.0); - float colA = 0.0; - - for ( int i=0; i = 1; + +uniform bool offsetHeight = true; +uniform bool invertR = false; +uniform bool invertG = false; +uniform bool invertH = false; + +uniform int type< + string label = "Filter Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Sobel"; + int option_1_value = 1; + string option_1_label = "Scharr"; +> = 0; + +float4 mainImage( VertData v_in ) : TARGET { + float2 step = float2(1.0, 1.0) / uv_size; + float2 vUv = v_in.uv; + + float dz = 1 / strength; + float dz2 = dz * dz; + + float2 tlv = float2(vUv.x - step.x, vUv.y + step.y); + float2 lv = float2(vUv.x - step.x, vUv.y ); + float2 blv = float2(vUv.x - step.x, vUv.y - step.y); + float2 tv = float2(vUv.x , vUv.y + step.y); + float2 bv = float2(vUv.x , vUv.y - step.y); + float2 trv = float2(vUv.x + step.x, vUv.y + step.y); + float2 rv = float2(vUv.x + step.x, vUv.y ); + float2 brv = float2(vUv.x + step.x, vUv.y - step.y); + + tlv = float2(tlv.x >= 0.0 ? tlv.x : (1.0 + tlv.x), tlv.y >= 0.0 ? tlv.y : (1.0 + tlv.y)); + tlv = float2(tlv.x < 1.0 ? tlv.x : (tlv.x - 1.0 ), tlv.y < 1.0 ? tlv.y : (tlv.y - 1.0 )); + lv = float2( lv.x >= 0.0 ? lv.x : (1.0 + lv.x), lv.y >= 0.0 ? lv.y : (1.0 + lv.y)); + lv = float2( lv.x < 1.0 ? lv.x : ( lv.x - 1.0 ), lv.y < 1.0 ? lv.y : ( lv.y - 1.0 )); + blv = float2(blv.x >= 0.0 ? blv.x : (1.0 + blv.x), blv.y >= 0.0 ? blv.y : (1.0 + blv.y)); + blv = float2(blv.x < 1.0 ? blv.x : (blv.x - 1.0 ), blv.y < 1.0 ? blv.y : (blv.y - 1.0 )); + tv = float2( tv.x >= 0.0 ? tv.x : (1.0 + tv.x), tv.y >= 0.0 ? tv.y : (1.0 + tv.y)); + tv = float2( tv.x < 1.0 ? tv.x : ( tv.x - 1.0 ), tv.y < 1.0 ? tv.y : ( tv.y - 1.0 )); + bv = float2( bv.x >= 0.0 ? bv.x : (1.0 + bv.x), bv.y >= 0.0 ? bv.y : (1.0 + bv.y)); + bv = float2( bv.x < 1.0 ? bv.x : ( bv.x - 1.0 ), bv.y < 1.0 ? bv.y : ( bv.y - 1.0 )); + trv = float2(trv.x >= 0.0 ? trv.x : (1.0 + trv.x), trv.y >= 0.0 ? trv.y : (1.0 + trv.y)); + trv = float2(trv.x < 1.0 ? trv.x : (trv.x - 1.0 ), trv.y < 1.0 ? trv.y : (trv.y - 1.0 )); + rv = float2( rv.x >= 0.0 ? rv.x : (1.0 + rv.x), rv.y >= 0.0 ? rv.y : (1.0 + rv.y)); + rv = float2( rv.x < 1.0 ? rv.x : ( rv.x - 1.0 ), rv.y < 1.0 ? rv.y : ( rv.y - 1.0 )); + brv = float2(brv.x >= 0.0 ? brv.x : (1.0 + brv.x), brv.y >= 0.0 ? brv.y : (1.0 + brv.y)); + brv = float2(brv.x < 1.0 ? brv.x : (brv.x - 1.0 ), brv.y < 1.0 ? brv.y : (brv.y - 1.0 )); + + float tl = image.Sample(textureSampler, tlv).r; + float l = image.Sample(textureSampler, lv ).r; + float bl = image.Sample(textureSampler, blv).r; + float t = image.Sample(textureSampler, tv ).r; + float b = image.Sample(textureSampler, bv ).r; + float tr = image.Sample(textureSampler, trv).r; + float r = image.Sample(textureSampler, rv ).r; + float br = image.Sample(textureSampler, brv).r; + + float dx = 0.0; + float dy = 0.0; + + if(type == 0) { // Sobel + dx = tl + l*2.0 + bl - tr - r*2.0 - br; + dy = tl + t*2.0 + tr - bl - b*2.0 - br; + } + else { // Scharr + dx = tl*3.0 + l*10.0 + bl*3.0 - tr*3.0 - r*10.0 - br*3.0; + dy = tl*3.0 + t*10.0 + tr*3.0 - bl*3.0 - b*10.0 - br*3.0; + } + + float invH = invertH ? -1. : 1.; + float invR = invertR ? -1. : 1.; + float invG = invertG ? -1. : 1.; + + float4 normal = float4( + float3(dx * invR * invH, dy * invG * invH, dz), + image.Sample(textureSampler, vUv).a + ); + + l = sqrt((dx * dx) + (dy * dy) + dz2); + + if (offsetHeight) { + return float4(normal.xy / l * 0.5 + 0.5, normal.zw); + } + + return float4(normal.xyz / l * 0.5 + 0.5, normal.w); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -23932,22 +29934,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaUVDistortionShader { +function Get-OBSOpacityShader { -[Alias('Set-OBSChromaUVDistortionShader','Add-OBSChromaUVDistortionShader')] +[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] param( -# Set the distortion of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('distortion')] -[Single] -$Distortion, -# Set the amplitude of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the chroma of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('chroma')] +# Set the Opacity of OBSOpacityShader +[ComponentModel.DefaultBindingProperty('Opacity')] [Single] -$Chroma, +$Opacity, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -23978,69 +29972,27 @@ $UseShaderTime process { -$shaderName = 'Chroma+UV-Distortion' -$ShaderNoun = 'OBSChromaUVDistortionShader' +$shaderName = 'opacity' +$ShaderNoun = 'OBSOpacityShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/WsdyRN +// Opacity shader - for OBS Shaderfilter +// Copyright 2021 by SkeltonBowTV +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv -//Higher values = less distortion -uniform float distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 5.0; - float maximum = 1000.0; - float step = 0.01; -> = 75.; -//Higher values = tighter distortion -uniform float amplitude< - string label = "Amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 10.; -//Higher values = more color distortion -uniform float chroma< - string label = "Chroma"; +uniform float Opacity< + string label = "Opacity"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 6.28318531; + float maximum = 200.0; float step = 0.01; -> = .5; - -float2 zoomUv(float2 uv, float zoom) { - float2 uv1 = uv; - uv1 += .5; - uv1 += zoom/2.-1.; - uv1 /= zoom; - return uv1; -} +> = 100.00; // 0.00 - 100.00 percent -float4 mainImage(VertData v_in) : TARGET +float4 mainImage( VertData v_in ) : TARGET { - float2 uvt = v_in.uv; - - float2 uvtR = uvt; - float2 uvtG = uvt; - float2 uvtB = uvt; - - //Uncomment the following line to get varying chroma distortion - //chroma = sin(elapsed_time)/2.+.5; - - uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); - uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); - uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); - - float2 uvR = zoomUv(uvtR, 1.1); - float2 uvG = zoomUv(uvtG, 1.1); - float2 uvB = zoomUv(uvtB, 1.1); - - float4 colR = image.Sample(textureSampler, uvR); - float4 colG = image.Sample(textureSampler, uvG); - float4 colB = image.Sample(textureSampler, uvB); - - return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); + float4 color = image.Sample( textureSampler, v_in.uv ); + return float4( color.rgb, color.a * Opacity * 0.01 ); } ' } @@ -24139,38 +30091,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCircleMaskFilterShader { +function Get-OBSPagePeelShader { -[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] +[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] param( -# Set the Radius of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Radius')] +# Set the Speed of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Speed')] [Single] -$Radius, -# Set the Circle_Offset_X of OBSCircleMaskFilterShader -[Alias('Circle_Offset_X')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] -[Int32] -$CircleOffsetX, -# Set the Circle_Offset_Y of OBSCircleMaskFilterShader -[Alias('Circle_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] -[Int32] -$CircleOffsetY, -# Set the Source_Offset_X of OBSCircleMaskFilterShader -[Alias('Source_Offset_X')] -[ComponentModel.DefaultBindingProperty('Source_Offset_X')] -[Int32] -$SourceOffsetX, -# Set the Source_Offset_Y of OBSCircleMaskFilterShader -[Alias('Source_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] -[Int32] -$SourceOffsetY, -# Set the Antialiasing of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Antialiasing')] -[Management.Automation.SwitchParameter] -$Antialiasing, +$Speed, +# Set the Position of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Position')] +[Single] +$Position, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -24201,85 +30133,84 @@ $UseShaderTime process { -$shaderName = 'circle-mask-filter' -$ShaderNoun = 'OBSCircleMaskFilterShader' +$shaderName = 'page-peel' +$ShaderNoun = 'OBSPagePeelShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Circle Mask Filter version 1.01, for OBS Shaderfilter -// Copyright 2022 by SkeletonBow -// Twitter: -// Twitch: -// License: GNU GPLv2 +// Simple Page Peel, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Based on Shadertoy shader by droozle +// +// Description: +// // // Changelog: -// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. -// 1.0 - Initial release +// 0.01 - Initial release -uniform float Radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform int Circle_Offset_X< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Circle_Offset_Y< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Source_Offset_X< - string label = "Source Offset X"; +uniform float Speed< + string label = "Speed"; string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0.0; -uniform int Source_Offset_Y< - string label = "Source Offset Y"; + float minimum = 0.0; + float maximum = 50.0; + float step = 0.001; +> = 1.00; +uniform float Position< + string label = "Position"; string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; > = 0.0; -uniform bool Antialiasing = true; -#define Smoothness 100.00 -#define AAwidth 4 - -#define uv_pi uv_pixel_interval - float4 mainImage( VertData v_in ) : TARGET { + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); float2 uv = v_in.uv; - float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; - float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; - float radius = Radius * 0.01; - float smwidth = radius * Smoothness * 0.01; - - float4 obstex = image.Sample( textureSampler, uv - soffset); - float4 color = obstex; - // Account for aspect ratio - uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; - float2 cuv = 0.5 + coffset; - float dist = distance(cuv,uv); - // Anti-aliased or pixelated edge - if( Antialiasing ) { - color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); - } else { - color.a = step( dist, radius ); - } + float t = Position + elapsed_time * Speed; + // Define the fold. + float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - return float4(color.rgb, color.a); + // Sample texture. + float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. + + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + col *= 0.25; // Background color (peeled off). + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); + } + else { + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + float4 back = image.Sample( textureSampler, pt ); // Back color. + back.rgb = back.rgb * 0.25 + 0.75; + + float shadow = smoothstep( 0.0, 0.2, -side ); + back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); + + // Support for transparency. + col = lerp( col, back.rgb, back.a ); + } + } + + // Output to screen + return float4(col,1.0); } ' @@ -24379,65 +30310,40 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockAnalogShader { +function Get-OBSPagePeelTransitionShader { -[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] +[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] param( -# Set the current_time_ms of OBSClockAnalogShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockAnalogShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockAnalogShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockAnalogShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the hour_handle_color of OBSClockAnalogShader -[Alias('hour_handle_color')] -[ComponentModel.DefaultBindingProperty('hour_handle_color')] -[Single[]] -$HourHandleColor, -# Set the minute_handle_color of OBSClockAnalogShader -[Alias('minute_handle_color')] -[ComponentModel.DefaultBindingProperty('minute_handle_color')] -[Single[]] -$MinuteHandleColor, -# Set the second_handle_color of OBSClockAnalogShader -[Alias('second_handle_color')] -[ComponentModel.DefaultBindingProperty('second_handle_color')] -[Single[]] -$SecondHandleColor, -# Set the outline_color of OBSClockAnalogShader -[Alias('outline_color')] -[ComponentModel.DefaultBindingProperty('outline_color')] -[Single[]] -$OutlineColor, -# Set the top_line_color of OBSClockAnalogShader -[Alias('top_line_color')] -[ComponentModel.DefaultBindingProperty('top_line_color')] -[Single[]] -$TopLineColor, -# Set the background_color of OBSClockAnalogShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[Single[]] -$BackgroundColor, -# Set the time_offset_hours of OBSClockAnalogShader -[Alias('time_offset_hours')] -[ComponentModel.DefaultBindingProperty('time_offset_hours')] -[Int32] -$TimeOffsetHours, +# Set the image_a of OBSPagePeelTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSPagePeelTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSPagePeelTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSPagePeelTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the page_color of OBSPagePeelTransitionShader +[Alias('page_color')] +[ComponentModel.DefaultBindingProperty('page_color')] +[String] +$PageColor, +# Set the page_transparency of OBSPagePeelTransitionShader +[Alias('page_transparency')] +[ComponentModel.DefaultBindingProperty('page_transparency')] +[Single] +$PageTransparency, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -24468,168 +30374,77 @@ $UseShaderTime process { -$shaderName = 'clock_analog' -$ShaderNoun = 'OBSClockAnalogShader' +$shaderName = 'page-peel-transition' +$ShaderNoun = 'OBSPagePeelTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Based on https://www.shadertoy.com/view/XdKXzy -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform float3 hour_handle_color = {1.0,1.0,1.0}; -uniform float3 minute_handle_color = {1.0,1.0,1.0}; -uniform float3 second_handle_color = {1.0,0.0,0.0}; -uniform float3 outline_color = {1.0,1.0,1.0}; -uniform float3 top_line_color = {1.0,0.0,0.0}; -uniform float3 background_color = {.5,.5,.5}; -uniform int time_offset_hours = 0; - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif -// this is my first try to actually use glsl almost from scratch -// so far all i''ve done is learning by doing / reading glsl docs. -// this is inspired by my non glsl „elapsed_time“ projects -// especially this one: https://www.gottz.de/analoguhr.htm - -// i will most likely use a buffer in future to calculate the elapsed_time -// aswell as to draw the background of the clock only once. -// tell me if thats a bad idea. - -// update: -// screenshot: http://i.imgur.com/dF0nHDk.png -// as soon as i think its in a usefull state i''ll release the source -// of that particular c++ application on github. -// i hope sommeone might find it usefull :D - -#define PI 3.141592653589793238462643383 +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; +uniform float page_transparency< + string label = "Page Transparency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; -// from https://www.shadertoy.com/view/4s3XDn <3 -float ln(float2 p, float2 a, float2 b) +float4 mainImage(VertData v_in) : TARGET { - float2 pa = p - a; - float2 ba = b - a; - float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * h); -} + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); + float2 uv = v_in.uv; -// i think i should spend some elapsed_time reading docs in order to minimize this. -// hints apreciated -// (Rotated LiNe) -float rln(float2 uv, float start, float end, float perc) { - float inp = perc * PI * 2.0; - float2 coord = float2(sin(inp), cos(inp)); - return ln(uv, coord * start, coord * end); -} + float t = transition_time * 12.0 + 11.0; + // Define the fold. + float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); -// i need this to have an alphachannel in the output -// i intend to use an optimized version of this shader for a transparent desktop widget experiment -float4 mixer(float4 c1, float4 c2) { - // please tell me if you think this would boost performance. - // the elapsed_time i implemented mix myself it sure did reduce - // the amount of operations but i''m not sure now - // if (c2.a <= 0.0) return c1; - // if (c2.a >= 1.0) return c2; - return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); - // in case you are curious how you could implement mix yourself: - // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); -} + // Sample texture. + float3 col = float3(1.0,0.0,0.0); -float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { - if (dist <= width + shadow) { - // lets draw the shadow - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); - // now lets draw the antialiased handle - color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + // Next page + col = image_b.Sample( textureSampler, uv ).rgb; + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); } - return color; -} + else { + -float4 mainImage(VertData v_in) : TARGET -{ - float2 R = uv_size; - // calculate the size of a pixel - float px = 1.0 / R.y; - // create percentages of the coordinate system - float2 p = (v_in.uv * uv_size).xy / R; - // center the scene and add perspective - float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); - - /*float2 uv = -1.0 + 2.0 * p.xy; - // lets add perspective for mobile device support - if (uv_size.x > uv_size.y) - uv.x *= uv_size.x / uv_size.y; - else - uv.y *= uv_size.y / uv_size.x;*/ - - // lets scale the scene a bit down: - uv *= 1.1; - px *= 0.9; - - float width = 0.015; - float dist = 1.0; - float centerdist = length(uv); - - float4 color = image.Sample(textureSampler, v_in.uv); - - // background of the clock - if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); - - float isRed = 1.0; - - if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { - // minute bars - for (float i = 0.0; i <= 15.0; i += 1.0) { - if (mod(i, 5.0) == 0.0) { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); - // draw first bar red - if (i == 0.0 && uv.y > 0.0) { - isRed = dist; - dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); - color = mixer(color, float4(top_line_color, dist)); - dist = 1.0; - } - } - else { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); - } + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + col = image_a.Sample( textureSampler, pt ).rgb; // Back color. + col = lerp(page_color.rgb, col, page_transparency); + + float shadow = smoothstep( 0.0, 0.2, -side ); + col = lerp( col * 0.2, col, shadow ); + }else{ + col = image_a.Sample( textureSampler, uv ).rgb; } - - // outline circle - dist = min(dist, abs(1.0-width-length(uv))); - // draw clock shadow - if (centerdist > 1.0) - color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); - - // draw outline + minute bars in white - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); - color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); - } - - if (centerdist < 1.0) { - float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); - // hour - color = styleHandle(color, px, - rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), - hour_handle_color, 0.03, 0.02); - - // minute - color = styleHandle(color, px, - rln(uv, -0.075, 0.7, elapsed_time / 3600.0), - minute_handle_color, 0.02, 0.02); - - // second - color = styleHandle(color, px, - min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), - second_handle_color, 0.01, 0.02); } - - return color; + // Output to screen + if(convert_linear) + col = srgb_nonlinear_to_linear(col); + return float4(col,1.0); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -24727,53 +30542,59 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalLedShader { +function Get-OBSPerlinNoiseShader { -[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] +[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] param( -# Set the current_time_sec of OBSClockDigitalLedShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalLedShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalLedShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the showMatrix of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showMatrix')] +# Set the speed of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the animated of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('animated')] [Management.Automation.SwitchParameter] -$ShowMatrix, -# Set the showOff of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showOff')] +$Animated, +# Set the apply_to_channel of OBSPerlinNoiseShader +[Alias('apply_to_channel')] +[ComponentModel.DefaultBindingProperty('apply_to_channel')] [Management.Automation.SwitchParameter] -$ShowOff, -# Set the ampm of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ampm')] +$ApplyToChannel, +# Set the inverted of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('inverted')] [Management.Automation.SwitchParameter] -$Ampm, -# Set the ledColor of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ledColor')] -[String] -$LedColor, -# Set the offsetHours of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] +$Inverted, +# Set the multiply of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('multiply')] +[Management.Automation.SwitchParameter] +$Multiply, +# Set the speed_horizonal of OBSPerlinNoiseShader +[Alias('speed_horizonal')] +[ComponentModel.DefaultBindingProperty('speed_horizonal')] +[Single] +$SpeedHorizonal, +# Set the speed_vertical of OBSPerlinNoiseShader +[Alias('speed_vertical')] +[ComponentModel.DefaultBindingProperty('speed_vertical')] +[Single] +$SpeedVertical, +# Set the iterations of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('iterations')] [Int32] -$OffsetSeconds, +$Iterations, +# Set the white_noise of OBSPerlinNoiseShader +[Alias('white_noise')] +[ComponentModel.DefaultBindingProperty('white_noise')] +[Single] +$WhiteNoise, +# Set the black_noise of OBSPerlinNoiseShader +[Alias('black_noise')] +[ComponentModel.DefaultBindingProperty('black_noise')] +[Single] +$BlackNoise, +# Set the notes of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -24804,215 +30625,257 @@ $UseShaderTime process { -$shaderName = 'clock_digital_led' -$ShaderNoun = 'OBSClockDigitalLedShader' +$shaderName = 'perlin_noise' +$ShaderNoun = 'OBSPerlinNoiseShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/MdfGzf -// cmarangu has linked all 7 segments in his comments -// https://www.shadertoy.com/view/3dtSRj - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif - -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; - -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; - -uniform bool showMatrix = false; -uniform bool showOff = false; -uniform bool ampm = false; -uniform float4 ledColor = {1.0,0,0,1.0}; -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; - -float segment(float2 uv, bool On) +// +// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +// +// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson +// Translation and modification was made by Keijiro Takahashi. +// Conversion for OBS by Charles Fettinger. +// +// This shader is based on the webgl-noise GLSL shader. For further details +// of the original shader, please see the following description from the +// original source code. +// + // +// GLSL textureless classic 2D noise "cnoise", (white_noise) +// with an RSL-style periodic variant "pnoise" (black_noise). +// Author: Stefan Gustavson (stefan.gustavson@liu.se) +// Version: 2011-08-22 +// +// Many thanks to Ian McEwan of Ashima Arts for the +// ideas for permutation and gradient selection. +// +// Copyright (c) 2011 Stefan Gustavson. All rights reserved. +// Distributed under the MIT license. See LICENSE file. +// https://github.com/ashima/webgl-noise +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 + float4 mod(float4 x, float4 y) { - if (!On && !showOff) - return 0.0; - - float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* - (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); - - //Fiddle with lights and matrix - //uv.x += sin(elapsed_time*60.0*6.26)/14.0; - //uv.y += cos(elapsed_time*60.0*6.26)/14.0; - - //led like brightness - if (On){ - seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; - } else { - seg *= -(0.05+length(uv*float2(0.2,0.1))); - } - return seg; + return x - y * floor(x / y); } - -float sevenSegment(float2 uv,int num) + float4 mod289(float4 x) { - float seg= 0.0; - seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); - seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); - seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); - seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); - seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); - seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); - seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); - - return seg; + return x - floor(x / 289.0) * 289.0; } - -float showNum(float2 uv,int nr, bool zeroTrim) + float4 permute(float4 x) { - //Speed optimization, leave if pixel is not in segment - if (abs(uv.x)>1.5 || abs(uv.y)>1.2) - return 0.0; - - float seg= 0.0; - if (uv.x>0.0) - { - nr /= 10; - if (nr==0 && zeroTrim) - nr = -1; - seg += sevenSegment(uv+float2(-0.75,0.0),nr); - } else { - seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); - } - - return seg; + return mod289(((x*34.0)+1.0)*x); } - -float dots(float2 uv) + float4 taylorInvSqrt(float4 r) { - float seg = 0.0; - uv.y -= 0.5; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - uv.y += 1.0; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - return seg; + return 1.79284291400159 - r * 0.85373472095314; } - -float4 mainImage(VertData v_in) : TARGET + float2 fade(float2 t) { + return t*t*t*(t*(t*6.0-15.0)+10.0); +} + // Classic Perlin noise +float cnoise(float2 P) { - float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / - min(uv_size.x,uv_size.y); - - if (uv_size.x>uv_size.y) - { - uv *= 6.0; - } - else - { - uv *= 12.0; - } - - uv.x *= -1.0; - uv.x += uv.y/12.0; - //wobble - //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; - //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; - uv.x += 3.5; - float seg = 0.0; - - if(timeMode == 0){ - seg += showNum(uv,current_time_sec,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - seg += showNum(uv,current_time_min,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - if (ampm) { - if(current_time_hour == 0){ - seg += showNum(uv,12,true); - }else if(current_time_hour > 12){ - seg += showNum(uv,current_time_hour-12,true); - }else{ - seg += showNum(uv,current_time_hour,true); - } - } else { - seg += showNum(uv,current_time_hour,true); - } - }else{ - float timeSecs = 0.0; - if(timeMode == 1){ - timeSecs = elapsed_time_enable; - }else if(timeMode == 2){ - timeSecs = elapsed_time_active; - }else if(timeMode == 3){ - timeSecs = elapsed_time_show; - }else if(timeMode == 4){ - timeSecs = elapsed_time_start; - } - - timeSecs += offsetSeconds + offsetHours*3600; - if(timeSecs < 0) - timeSecs = 0.9999-timeSecs; - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - - uv.x -= 1.75; - - seg += dots(uv); - - uv.x -= 1.75; - - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - if (ampm) - { - if(timeSecs == 0.0){ - timeSecs = 12.0; - }else if (timeSecs > 12.0){ - timeSecs = mod(timeSecs,12.0); - } - }else if (timeSecs > 24.0) { - timeSecs = mod(timeSecs,24.0); - } - - uv.x -= 1.75; - - seg += dots(uv); - - uv.x -= 1.75; - seg += showNum(uv,int(mod(timeSecs,60.0)),true); - } + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + // Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + //The good bits~ adapting the noise generator for the plugin and giving some control over the shader + //todo: pseudorandom number generator w/ seed +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform bool animated; +uniform bool apply_to_channel; +uniform bool inverted; +uniform bool multiply; +uniform float speed_horizonal< + string label = "Speed horizontal"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform float speed_vertical< + string label = "Speed vertical"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0; +uniform int iterations< + string label = "Iterations"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 4; +//how much c_noise do we want? white +uniform float white_noise< + string label = "White noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +//how much p_noise do we want? black +uniform float black_noise< + string label = "Black noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform string notes< + string widget_type = "info"; +> = "white noise and black noise and iterations.. enjoy!"; + float2 noisePosition(float t){ + return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); +} + float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float t = elapsed_time * speed; + float2 dir = float2(speed_horizonal,speed_vertical); - if (seg==0.0){ - return image.Sample(textureSampler, v_in.uv); - } - // matrix over segment - if (showMatrix) - { - seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); - //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); - } - if (seg<0.0) - { - seg = -seg;; - return float4(seg,seg,seg,1.0); + if(!animated){ + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = v_in.uv * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + //o += pnoise(coord, period) * w; + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } + } else { + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = (v_in.uv + t*dir) * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } } - return float4(ledColor.rgb * seg, ledColor.a); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -25110,62 +30973,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalNixieShader { +function Get-OBSPerspectiveShader { -[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] +[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] param( -# Set the current_time_ms of OBSClockDigitalNixieShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockDigitalNixieShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalNixieShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalNixieShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the offsetHours of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] -[Int32] -$OffsetSeconds, -# Set the corecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('corecolor')] -[Single[]] -$Corecolor, -# Set the halocolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('halocolor')] -[Single[]] -$Halocolor, -# Set the flarecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('flarecolor')] -[Single[]] -$Flarecolor, -# Set the anodecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodecolor')] -[Single[]] -$Anodecolor, -# Set the anodehighlightscolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] -[Single[]] -$Anodehighlightscolor, +# Set the angle_x of OBSPerspectiveShader +[Alias('angle_x')] +[ComponentModel.DefaultBindingProperty('angle_x')] +[Single] +$AngleX, +# Set the angle_y of OBSPerspectiveShader +[Alias('angle_y')] +[ComponentModel.DefaultBindingProperty('angle_y')] +[Single] +$AngleY, +# Set the angle_z of OBSPerspectiveShader +[Alias('angle_z')] +[ComponentModel.DefaultBindingProperty('angle_z')] +[Single] +$AngleZ, +# Set the perspective of OBSPerspectiveShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the border_color of OBSPerspectiveShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the show_border of OBSPerspectiveShader +[Alias('show_border')] +[ComponentModel.DefaultBindingProperty('show_border')] +[Management.Automation.SwitchParameter] +$ShowBorder, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -25196,483 +31036,104 @@ $UseShaderTime process { -$shaderName = 'clock_digital_nixie' -$ShaderNoun = 'OBSClockDigitalNixieShader' +$shaderName = 'perspective' +$ShaderNoun = 'OBSPerspectiveShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/fsBcRm -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; - -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; - -// Colors as named variables, if you want to tweak them -uniform float3 corecolor = {1.0,0.7,0.0}; -uniform float3 halocolor = {1.0,0.5,0.0}; -uniform float3 flarecolor = {1.0,0.3,0.0}; -uniform float3 anodecolor = {0.2,0.1,0.1}; -uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; +// Perspective Transform Shader for OBS +// Allows adjustable 3D perspective effects +// Usage: Add as filter in OBS via ShaderFilter plugin -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#define lessThan(a,b) (a < b) -#define greaterThan(a,b) (a > b) -#endif +uniform float angle_x< + string label = "X Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; -// psrdnoise (c) Stefan Gustavson and Ian McEwan, -// ver. 2021-12-02, published under the MIT license: -// https://github.com/stegu/psrdnoise/ -float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) -{ - float2 uv = float2(x.x+x.y*0.5, x.y); - float2 i0 = floor(uv), f0 = frac(uv); - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - float2 i1 = i0 + o1, i2 = i0 + 1.0; - float2 v0 = float2(i0.x - i0.y*0.5, i0.y); - float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); - float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); - float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; - float3 iu, iv, xw, yw; - if(any(greaterThan(period, float2(0.0,0.0)))) { - xw = float3(v0.x, v1.x, v2.x); - yw = float3(v0.y, v1.y, v2.y); - if(period.x > 0.0) - xw = mod(float3(v0.x, v1.x, v2.x), period.x); - if(period.y > 0.0) - yw = mod(float3(v0.y, v1.y, v2.y), period.y); - iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); - } else { - iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); - } - float3 hash = mod(iu, 289.0); - hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); - hash = mod((hash*34.0 + 10.0)*hash, 289.0); - float3 psi = hash*0.07482 + alpha; - float3 gx = cos(psi); float3 gy = sin(psi); - float2 g0 = float2(gx.x, gy.x); - float2 g1 = float2(gx.y, gy.y); - float2 g2 = float2(gx.z, gy.z); - float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); - w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; - float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); - float n = dot(w4, gdotx); - float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; - float2 dn0 = w4.x*g0 + dw.x*x0; - float2 dn1 = w4.y*g1 + dw.y*x1; - float2 dn2 = w4.z*g2 + dw.z*x2; - gradient = 10.9*(dn0 + dn1 + dn2); - return 10.9*n; -} +uniform float angle_y< + string label = "Y Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; -// Compute the shortest distance from p -// to a line segment from p1 to p2. -float lined(float2 p1, float2 p2, float2 p) { - float2 p1p2 = p2 - p1; - float2 v = normalize(p1p2); - float2 s = p - p1; - float t = dot(v, s); - if (t<0.0) return length(s); - if (t>length(p1p2)) return length(p - p2); - return length(s - t*v); -} +uniform float angle_z< + string label = "Z Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; -// Compute the shortest distance from p to a circle -// with center at c and radius r. (Extremely simple.) -float circled(float2 c, float r, float2 p) { - return abs(length(p - c) - r); -} +uniform float perspective< + string label = "Perspective Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; -// Compute the shortest distance from p to a -// circular arc with center c from p1 to p2. -// p1, p2 are in the +angle direction (ccw), -// to resolve the major/minor arc ambiguity, so -// specifying p1, p2 in the wrong order will -// yield the complement to the arc you wanted. -// If p1 = p2, the entire circle is drawn, but -// you don''t want to use this function to draw -// a circle. Use the simple circled() instead. -// If p1 and p2 have different distances to c, -// the end of the arc will not look right. If -// this is inconvenient, uncomment the 3rd line. -float arcd(float2 c, float2 p1, float2 p2, float2 p) { +uniform float4 border_color< + string label = "Border Color"; +> = {0.0, 0.0, 0.0, 1.0}; - float2 v1 = p1 - c; - float2 v2 = p2 - c; - // Optional: make sure p1, p2 are both on the circle - // v2 = normalize(v2)*length(v1); - float2 v = p - c; +uniform bool show_border< + string label = "Show Border"; +> = true; - float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); +float4x4 rotationMatrix(float3 angles) +{ + float radX = radians(angles.x); + float radY = radians(angles.y); + float radZ = radians(angles.z); + + float sinX = sin(radX); + float cosX = cos(radX); + float sinY = sin(radY); + float cosY = cos(radY); + float sinZ = sin(radZ); + float cosZ = cos(radZ); + + return float4x4( + cosY*cosZ, -cosY*sinZ, sinY, 0, + sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, + -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, + 0, 0, 0, 1 + ); +} - if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi - if(all(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); // nearest end - } else { - return abs(length(v) - length(v1)); // dist to arc - } - } else { // Arc angle > pi - if(any(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); - } else { - return abs(length(v) - length(v1)); - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + + // Center coordinates + float2 center = float2(0.5, 0.5); + uv -= center; + + // Apply perspective + float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); + uv *= perspectiveFactor; + + // Create rotation matrix + float3 angles = float3(angle_x, angle_y, angle_z); + float4x4 rotMat = rotationMatrix(angles); + + // Apply transformation + float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); + + // Restore center position + uv = transformed.xy + center; + + // Sample texture with border handling + if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { + return show_border ? border_color : float4(0, 0, 0, 0); } + + return image.Sample(textureSampler, uv); } - -// A convenient anti-aliased step() using auto derivatives -float aastep(float threshold, float value) { - float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); - return smoothstep(threshold-afwidth, threshold+afwidth, value); -} - -// A smoothstep() that blends to an aastep() under minification -float aasmoothstep(float t1, float t2, float v) { - float aw = 0.7 * length(float2(ddx(v), ddy(v))); - float sw = max(0.5*(t2-t1), aw); - float st = 0.5*(t1+t2); - return smoothstep(st-sw, st+sw, v); -} - -// Distance field of a hexagonal (simplex) grid -// The return vector contains the distances to the -// three closest points, sorted by magnitude. -float3 hexgrid(float2 p) { - - const float stretch = 1.0/0.8660; - - // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) - p.y = p.y * stretch; - // Transform to grid space (axis-aligned "simplex" grid) - float2 uv = float2(p.x + p.y*0.5, p.y); - // Determine which simplex we''re in, with i0 being the "base" - float2 i0 = floor(uv); - float2 f0 = frac(uv); - // o1 is the offset in simplex space to the second corner - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - // Enumerate the remaining simplex corners - float2 i1 = i0 + o1; - float2 i2 = i0 + float2(1.0, 1.0); - // Transform corners back to texture space - float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); - float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); - float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); - float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); - // Only three values - bubble sort is just fine. - d.yz = (d.y < d.z) ? d.yz : d.zy; - d.xy = (d.x < d.y) ? d.xy : d.yx; - d.yz = (d.y < d.z) ? d.yz : d.zy; - return d; -} - -// The digits. Simple functions, only a lot of them. - -// These glyphs and their implementation as distance fields -// are the original work of me (stefan.gustavson@gmail.com), -// and the code below is released under the MIT license: -// https://opensource.org/licenses/MIT -// (If that is inconvenient for you, let me know. I''m reasonable.) -// -// Experts say mortals should not attempt to design character shapes. -// "It''s just ten simple digits", I thought, "How hard can it be?" -// A week later, after countless little tweaks to proportions and -// curvature, and with a notepad full of sketches and pen-and-paper -// math, some of it horribly wrong because it was decades since I -// solved this kind of equations by hand, I know the answer: -// It can be *really* hard. But also loads of fun! -// -float nixie0(float2 p) { - // Special hack instead of pasting together arcs and lines - float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); - return abs(d - 2.0); -} - -float nixie1(float2 p) { - float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); - float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); - return min(d1, d2); -} - -float nixie1alt(float2 p) { // Straight line - return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); -} - -float nixie2(float2 p) { - const float x = 3.2368345; // Icky coordinates, - const float y = 4.4283002; // used twice below - float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); - float d2 = arcd(float2(10.657842, -5.001899), // Also icky - float2(x, y), float2(-0.25, 0.0), p); - float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} - -float nixie2alt(float2 p) { // Straight neck - float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); - float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); - float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} - -float nixie3(float2 p) { - // Two round parts: - // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); - // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); - // Angled top, more like classic Nixie tube digits: - float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); - float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); - return min(min(d1, d2), d3); -} - -float nixie3alt(float2 p) { // Two round parts of the same size - float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); - float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); - return min(d1, d2); -} - -float nixie4(float2 p) { - // This digit is 5.0 units wide, most others are 4.0 or 4.5 - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); - return min(min(d1, d2), d3); -} - -float nixie4alt(float2 p) { - // This digit is 4.0 units wide, but looks cropped - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(d1, d2), d3); -} - -float nixie5(float2 p) { - float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); - float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); - float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); - float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - return min(min(d1, d2), min(d3, d4)); -} - -float nixie5alt(float2 p) { - float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); - float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); - float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); - float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); - float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); - float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); -} - -float nixie6(float2 p) { - float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); - float d2 = circled(float2(2.0, 2.25), 2.25, p); - return min(d1, d2); -} - -float nixie6alt(float2 p) { // Straight neck - float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); - float d2 = circled(float2(2.0,2.0), 2.0, p); - return min(d1, d2); -} - -float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact - float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); - return min(min(d1, d2), d3); -} - -float nixie7alt(float2 p) { // Straight neck - float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); - return min(d1, d2); -} - -float nixie8(float2 p) { - float d1 = circled(float2(2.0, 2.2), 2.2, p); - float d2 = circled(float2(2.0, 6.2), 1.8, p); - return min(d1, d2); -} - -float nixie8alt(float2 p) { // Same size loops - float d1 = circled(float2(2.0,2.0), 2.0, p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} - -float nixie9(float2 p) { - float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); - float d2 = circled(float2(2.0, 5.75), 2.25, p); - return min(d1, d2); -} - -float nixie9alt(float2 p) { // Straight neck - float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} - -float nixieminus(float2 p) { - return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); -} - -float nixieequals(float2 p) { - float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); - float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); - return min(d1, d2); -} - -float nixieplus(float2 p) { - float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); - float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); - return min(d1, d2); -} - -float nixiedot(float2 p) { - // circled with r=0 yields a point, but with more work - return length(p - float2(2.0, 0.0)); -} - -float nixiecolon(float2 p) { - float d1 = length(p - float2(2.0,2.0)); - float d2 = length(p - float2(2.0, 5.0)); - return min(d1, d2); -} - -// End of MIT-licensed code - -float number(int n, float2 p) { - switch(n) { - case 0: return nixie0(p); - case 1: return nixie1(p); - case 2: return nixie2(p); - case 3: return nixie3(p); - case 4: return nixie4(p); - case 5: return nixie5(p); - case 6: return nixie6(p); - case 7: return nixie7(p); - case 8: return nixie8(p); - case 9: return nixie9(p); - default: return 1e10; - } -} -// Display the current time with a retro Nixie-tube look -// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 -// All code in the "Image" tab is public domain. -// Functions in the "Common" tab are also public domain, -// except where a separate license is specified. -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = ((float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size)/uv_size.x); - float time = 0.0; - if(timeMode == 0){ - time = float(current_time_hour*3600+current_time_min*60+current_time_sec) + float(current_time_ms)/1000.0; - }else if(timeMode == 1){ - time = elapsed_time_enable; - }else if(timeMode == 2){ - time = elapsed_time_active; - }else if(timeMode == 3){ - time = elapsed_time_show; - }else if(timeMode == 4){ - time = elapsed_time_start; - } - time += offsetSeconds + offsetHours * 3600; - if(time < 0) - time = 0.9999-time; - float2 p = -3.0+uv*50.0 - float2(0.0,9.0); - - float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), - max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); - - // Some relief for the GPU: exit early if we''re in the black margins - if(bbox == 0.0) { - return float4(float3(0.0,0.0,0.0),1.0); - } - - // If not, well, let''s put that GPU to good use! - float secs = floor(mod(time, 60.0)); - float mins = floor(mod(time, 3600.0)/60.0); - float hrs = floor(time/3600.0); - int h10 = int(floor(hrs/10.0)); - int h1 = int(floor(mod(hrs, 10.0))); - int m10 = int(floor(mins/10.0)); - int m1 = int(floor(mod(mins, 10.0))); - int s10 = int(floor(secs/10.0)); - int s1 = int(floor(mod(secs, 10.0))); - - float2 wspace = float2(6.5, 0.0); - float2 nspace = float2(3.5, 0.0); - float d = 1e10; - d = min(d, number(h10, p)); - d = min(d, number(h1, p-wspace)); - d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); - d = min(d, number(m10, p-2.0*wspace-nspace)); - d = min(d, number(m1, p-3.0*wspace-nspace)); - d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); - d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); - d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); - - float2 g; // For gradients returned from psrdnoise() - - // Digit outlines - float core = 1.0-aastep(0.2, d); - // "flare" is a wide blurry region around the characters, and - // "flarenoise" is a spatio-temporal modulation of its extents - // (slight flickering, but not all characters at the same time) - float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); - float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); - flare *= flare; // A more rapid decline towards the edge - // "glow" is a variation in the intensity of the glowy cathode (core) - float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); - // Now we mess up the distance field a little for the "halo" effect - d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); - d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); - // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube - // doesn''t have this, but it adds some appealing visual detail. - // Looks more like hot filaments than "cold cathodes", but... oh, well. - float halo = 1.0-smoothstep(-0.3, 0.3, d); - - // Brittle parameters! This scale/shift of p has a strong impact - // on the pattern at the edges of the grid through "anodefade". - float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); - float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders - // Fade the hexagonal holes in the anode towards the edges - float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), - max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); - float anode = 1.0 - aastep(0.1, anodedist - anodefade); - - float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); - float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know - - float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black - mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); - mixcolor = lerp(mixcolor, halocolor, 0.9*halo); - mixcolor = lerp(mixcolor, corecolor, core*glow); - mixcolor = lerp(mixcolor, anodecolorresult, anode); - mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box - return float4(mixcolor,1.0); -} - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -25770,18 +31231,129 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorDepthShader { +function Get-OBSPieChartShader { -[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] +[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] param( -# Set the colorDepth of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('colorDepth')] +# Set the inner_radius of OBSPieChartShader +[Alias('inner_radius')] +[ComponentModel.DefaultBindingProperty('inner_radius')] [Single] -$ColorDepth, -# Set the pixelSize of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('pixelSize')] +$InnerRadius, +# Set the outer_radius of OBSPieChartShader +[Alias('outer_radius')] +[ComponentModel.DefaultBindingProperty('outer_radius')] [Single] -$PixelSize, +$OuterRadius, +# Set the start_angle of OBSPieChartShader +[Alias('start_angle')] +[ComponentModel.DefaultBindingProperty('start_angle')] +[Single] +$StartAngle, +# Set the total of OBSPieChartShader +[ComponentModel.DefaultBindingProperty('total')] +[Int32] +$Total, +# Set the part_1 of OBSPieChartShader +[Alias('part_1')] +[ComponentModel.DefaultBindingProperty('part_1')] +[Int32] +$Part1, +# Set the color_1 of OBSPieChartShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the part_2 of OBSPieChartShader +[Alias('part_2')] +[ComponentModel.DefaultBindingProperty('part_2')] +[Int32] +$Part2, +# Set the color_2 of OBSPieChartShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the part_3 of OBSPieChartShader +[Alias('part_3')] +[ComponentModel.DefaultBindingProperty('part_3')] +[Int32] +$Part3, +# Set the color_3 of OBSPieChartShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the part_4 of OBSPieChartShader +[Alias('part_4')] +[ComponentModel.DefaultBindingProperty('part_4')] +[Int32] +$Part4, +# Set the color_4 of OBSPieChartShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# Set the part_5 of OBSPieChartShader +[Alias('part_5')] +[ComponentModel.DefaultBindingProperty('part_5')] +[Int32] +$Part5, +# Set the color_5 of OBSPieChartShader +[Alias('color_5')] +[ComponentModel.DefaultBindingProperty('color_5')] +[String] +$Color5, +# Set the part_6 of OBSPieChartShader +[Alias('part_6')] +[ComponentModel.DefaultBindingProperty('part_6')] +[Int32] +$Part6, +# Set the color_6 of OBSPieChartShader +[Alias('color_6')] +[ComponentModel.DefaultBindingProperty('color_6')] +[String] +$Color6, +# Set the part_7 of OBSPieChartShader +[Alias('part_7')] +[ComponentModel.DefaultBindingProperty('part_7')] +[Int32] +$Part7, +# Set the color_7 of OBSPieChartShader +[Alias('color_7')] +[ComponentModel.DefaultBindingProperty('color_7')] +[String] +$Color7, +# Set the part_8 of OBSPieChartShader +[Alias('part_8')] +[ComponentModel.DefaultBindingProperty('part_8')] +[Int32] +$Part8, +# Set the color_8 of OBSPieChartShader +[Alias('color_8')] +[ComponentModel.DefaultBindingProperty('color_8')] +[String] +$Color8, +# Set the part_9 of OBSPieChartShader +[Alias('part_9')] +[ComponentModel.DefaultBindingProperty('part_9')] +[Int32] +$Part9, +# Set the color_9 of OBSPieChartShader +[Alias('color_9')] +[ComponentModel.DefaultBindingProperty('color_9')] +[String] +$Color9, +# Set the part_10 of OBSPieChartShader +[Alias('part_10')] +[ComponentModel.DefaultBindingProperty('part_10')] +[Int32] +$Part10, +# Set the color_10 of OBSPieChartShader +[Alias('color_10')] +[ComponentModel.DefaultBindingProperty('color_10')] +[String] +$Color10, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -25812,43 +31384,162 @@ $UseShaderTime process { -$shaderName = 'color-depth' -$ShaderNoun = 'OBSColorDepthShader' +$shaderName = 'pie-chart' +$ShaderNoun = 'OBSPieChartShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/tscfWM -uniform float colorDepth< - string label = "Color depth"; +uniform float inner_radius< + string label = "inner radius"; string widget_type = "slider"; - float minimum = 0.01; + float minimum = 0.0; float maximum = 100.0; - float step = 0.01; -> = 5.0; - -uniform float pixelSize< - string label = "Pixel Size"; + float step = 0.1; +> = 32.0; +uniform float outer_radius< + string label = "outer radius"; string widget_type = "slider"; - float minimum = 1.0; + float minimum = 0.0; float maximum = 100.0; - float step = 0.01; -> = 5.0; - + float step = 0.1; +> = 50.0; +uniform float start_angle< + string label = "Start angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 90.0; -float4 mainImage(VertData v_in) : TARGET -{ - // Change these to change results - float2 size = uv_size / pixelSize; - float2 uv = v_in.uv; - // Maps UV onto grid of variable size to pixilate the image - uv = round(uv*size)/size; - float4 col = image.Sample(textureSampler, uv); - // Maps color onto the specified color depth - return float4(round(col.r * colorDepth) / colorDepth, - round(col.g * colorDepth) / colorDepth, - round(col.b * colorDepth) / colorDepth, 1.0); -} -' -} +uniform int total< + string label = "Total"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 100; +uniform int part_1< + string label = "Part 1"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 50; +uniform float4 color_1 = {0.0,0.26,0.62,1.0}; +uniform int part_2< + string label = "Part 2"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 25; +uniform float4 color_2 = {0.24,0.40,0.68,1.0}; +uniform int part_3< + string label = "Part 3"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 10; +uniform float4 color_3 = {0.38,0.56,0.75,1.0}; +uniform int part_4< + string label = "Part 4"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 5; +uniform float4 color_4 = {0.52,0.72,0.81,1.0}; +uniform int part_5< + string label = "Part 5"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 3; +uniform float4 color_5 = {0.69,0.87,0.86,1.0}; +uniform int part_6< + string label = "Part 6"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 2; +uniform float4 color_6 = {1.0,0.79,0.73,1.0}; +uniform int part_7< + string label = "Part 7"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_7 = {0.99,0.57,0.57,1.0}; +uniform int part_8< + string label = "Part 8"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_8 = {0.91,0.36,0.44,1.0}; +uniform int part_9< + string label = "Part 9"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_9 = {0.77,0.16,0.32,1.0}; +uniform int part_10< + string label = "Part 10"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 0; +uniform float4 color_10 = {0.58,0.0,0.23,1.0}; + +float4 mainImage(VertData v_in) : TARGET +{ + const float pi = 3.14159265358979323846; +#ifdef OPENGL + float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); + float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); +#else + float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; + float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; +#endif + float2 center = float2(0.5, 0.5); + float2 factor; + if(uv_size.x < uv_size.y){ + factor = float2(1.0, uv_size.y/uv_size.x); + }else{ + factor = float2(uv_size.x/uv_size.y, 1.0); + } + center = center * factor; + float d = distance(center, v_in.uv * factor); + if(d > outer_radius/100.0 || d < inner_radius/100.0){ + return image.Sample(textureSampler, v_in.uv); + } + float2 toCenter = center - v_in.uv*factor; + float angle = atan2(toCenter.y ,toCenter.x); + angle = angle - (start_angle / 180.0 * pi); + if(angle < 0.0) + angle = pi + pi + angle; + if(angle < 0.0) + angle = pi + pi + angle; + angle = angle / (pi + pi); + float t = 0.0; + for(int i = 0; i < 10; i+=1) { + float part = parts[i]/total; + if(angle > t && angle <= t+part){ + return colors[i]; + } + t = t + part; + } + return image.Sample(textureSampler, v_in.uv); +} +' +} $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { $myNoun = $myVerb @@ -25944,33 +31635,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorGradeFilterShader { +function Get-OBSPixelationShader { -[Alias('Set-OBSColorGradeFilterShader','Add-OBSColorGradeFilterShader')] +[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] param( -# Set the notes of OBSColorGradeFilterShader +# Set the Target_Width of OBSPixelationShader +[Alias('Target_Width')] +[ComponentModel.DefaultBindingProperty('Target_Width')] +[Single] +$TargetWidth, +# Set the Target_Height of OBSPixelationShader +[Alias('Target_Height')] +[ComponentModel.DefaultBindingProperty('Target_Height')] +[Single] +$TargetHeight, +# Set the notes of OBSPixelationShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, -# Set the lut of OBSColorGradeFilterShader -[ComponentModel.DefaultBindingProperty('lut')] -[String] -$Lut, -# Set the lut_amount_percent of OBSColorGradeFilterShader -[Alias('lut_amount_percent')] -[ComponentModel.DefaultBindingProperty('lut_amount_percent')] -[Int32] -$LutAmountPercent, -# Set the lut_scale_percent of OBSColorGradeFilterShader -[Alias('lut_scale_percent')] -[ComponentModel.DefaultBindingProperty('lut_scale_percent')] -[Int32] -$LutScalePercent, -# Set the lut_offset_percent of OBSColorGradeFilterShader -[Alias('lut_offset_percent')] -[ComponentModel.DefaultBindingProperty('lut_offset_percent')] -[Int32] -$LutOffsetPercent, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -26001,79 +31683,55 @@ $UseShaderTime process { -$shaderName = 'color_grade_filter' -$ShaderNoun = 'OBSColorGradeFilterShader' +$shaderName = 'pixelation' +$ShaderNoun = 'OBSPixelationShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Grade Filter by Charles Fettinger for obs-shaderfilter plugin 4/2020 -//https://github.com/Oncorporation/obs-shaderfilter -//OBS messed up the LUT system, this is basically the old LUT system -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform string notes< - string widget_type = "info"; -> = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; - -uniform texture2d lut< - string label = "LUT"; ->; -uniform int lut_amount_percent< - string label = "LUT amount percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_scale_percent< - string label = "LUT scale percentage"; +// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// with help from SkeltonBowTV +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 15, 2022 +uniform float Target_Width< + string label = "Target Width"; string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_offset_percent< - string label = "LUT offset percentage"; + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 320.0; +uniform float Target_Height< + string label = "Target Height"; string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 0; - + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 180.0; +uniform string notes< + string widget_type = "info"; +> = "adjust width and height to your screen dimension"; float4 mainImage(VertData v_in) : TARGET { - float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); - float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); - float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); - - float4 textureColor = image.Sample(textureSampler, v_in.uv); - float lumaLevel = textureColor.r * 0.2126 + textureColor.g * 0.7152 + textureColor.b * 0.0722; - float blueColor = float(lumaLevel);//textureColor.b * 63.0 - - float2 quad1; - quad1.y = floor(floor(float(blueColor)) / 8.0); - quad1.x = floor(float(blueColor)) - (quad1.y * 8.0); - - float2 quad2; - quad2.y = floor(ceil(float(blueColor)) / 8.0); - quad2.x = ceil(float(blueColor)) - (quad2.y * 8.0); + float targetWidth = Target_Width; + if(targetWidth < 2.0) + targetWidth = 2.0; + float targetHeight = Target_Height; + if(targetHeight < 2.0) + targetHeight = 2.0; + float2 tex1; + int pixelSizeX = int(uv_size.x / targetWidth); + int pixelSizeY = int(uv_size.y / targetHeight); - float2 texPos1; - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + int pixelX = int(v_in.uv.x * uv_size.x); + int pixelY = int(v_in.uv.y * uv_size.y); - float2 texPos2; - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; + tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; - float4 newColor1 = lut.Sample(textureSampler, texPos1); - newColor1.rgb = newColor1.rgb * lut_scale + lut_offset; - float4 newColor2 = lut.Sample(textureSampler, texPos2); - newColor2.rgb = newColor2.rgb * lut_scale + lut_offset; - float4 luttedColor = lerp(newColor1, newColor2, frac(float(blueColor))); + float4 c1 = image.Sample(textureSampler, tex1 ); - float4 final_color = lerp(textureColor, luttedColor, lut_amount); - return float4(final_color.rgb, textureColor.a); + return c1; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -26171,55 +31829,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCornerPinShader { +function Get-OBSPixelationTransitionShader { -[Alias('Set-OBSCornerPinShader','Add-OBSCornerPinShader')] +[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] param( -# Set the Antialias_Edges of OBSCornerPinShader -[Alias('Antialias_Edges')] -[ComponentModel.DefaultBindingProperty('Antialias_Edges')] -[Management.Automation.SwitchParameter] -$AntialiasEdges, -# Set the Top_Left_X of OBSCornerPinShader -[Alias('Top_Left_X')] -[ComponentModel.DefaultBindingProperty('Top_Left_X')] -[Single] -$TopLeftX, -# Set the Top_Left_Y of OBSCornerPinShader -[Alias('Top_Left_Y')] -[ComponentModel.DefaultBindingProperty('Top_Left_Y')] -[Single] -$TopLeftY, -# Set the Top_Right_X of OBSCornerPinShader -[Alias('Top_Right_X')] -[ComponentModel.DefaultBindingProperty('Top_Right_X')] -[Single] -$TopRightX, -# Set the Top_Right_Y of OBSCornerPinShader -[Alias('Top_Right_Y')] -[ComponentModel.DefaultBindingProperty('Top_Right_Y')] -[Single] -$TopRightY, -# Set the Bottom_Left_X of OBSCornerPinShader -[Alias('Bottom_Left_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] +# Set the transition_time of OBSPixelationTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] [Single] -$BottomLeftX, -# Set the Bottom_Left_Y of OBSCornerPinShader -[Alias('Bottom_Left_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +$TransitionTime, +# Set the convert_linear of OBSPixelationTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the power of OBSPixelationTransitionShader +[ComponentModel.DefaultBindingProperty('power')] [Single] -$BottomLeftY, -# Set the Bottom_Right_X of OBSCornerPinShader -[Alias('Bottom_Right_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +$Power, +# Set the center_x of OBSPixelationTransitionShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] [Single] -$BottomRightX, -# Set the Bottom_Right_Y of OBSCornerPinShader -[Alias('Bottom_Right_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +$CenterX, +# Set the center_y of OBSPixelationTransitionShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] [Single] -$BottomRightY, +$CenterY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -26250,191 +31887,64 @@ $UseShaderTime process { -$shaderName = 'corner-pin' -$ShaderNoun = 'OBSCornerPinShader' +$shaderName = 'pixelation-transition' +$ShaderNoun = 'OBSPixelationTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Corner Pin, Version 0.1, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Based on: -// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm -// https://www.youtube.com/c/InigoQuilez -// https://iquilezles.org/ -// and the derivative StreamFX Corner Pin effect by Xaymar -// https://github.com/Xaymar/obs-StreamFX -// -// Description: -// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily -// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying -// image with proper perspective without distortion. -// -// TODO: -// - Add feature to automatically quantize corners to pixel centers -// -// Changelog: -// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by -// Inigo Quilez that it was based upon. - -uniform bool Antialias_Edges = true; - -uniform float Top_Left_X< - string label = "Top left x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Left_Y< - string label = "Top left y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Right_X< - string label = "Top right x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Top_Right_Y< - string label = "Top right y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_X< - string label = "Bottom left x"; +uniform float transition_time< + string label = "Transittion Time"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_Y< - string label = "Bottom left y"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float power< + string label = "Power"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_X< - string label = "Bottom right x"; + float minimum = 0.5; + float maximum = 8.0; + float step = 0.01; +> = 3.0; +uniform float center_x< + string label = "X"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_Y< - string label = "Bottom right y"; + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Y"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; - -// DEVELOPMENTAL DEBUGGING OPTIONS -//uniform float AAstrength = 1.0; -//uniform float AAdist = 1.0; -//uniform float debug_psmult = 1.0; -//#define PIXEL_SIZE_MULT 2.0 - -float cross2d(in float2 a, in float2 b) -{ - return (a.x * b.y) - (a.y * b.x); -} + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; -float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) +float4 mainImage(VertData v_in) : TARGET { - 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); - } + //1..0..1 + float scale = abs(transition_time - 0.5) * 2.0; + scale = pow(scale, power); - return result; + float2 uv = v_in.uv; + uv -= float2(center_x, center_y); + uv *= uv_size; + uv *= scale; + uv = floor(uv); + uv /= scale; + uv /= uv_size; + uv += float2(center_x, center_y); + uv = clamp(uv, 1.0/uv_size, 1.0); + float4 rgba = image.Sample(textureSampler, uv); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; } -// distance to a line segment -float sdSegment( in float2 p, in float2 a, in float2 b ) -{ - p -= a; b -= a; - return length( p-b*saturate(dot(p,b)/dot(b,b)) ); -} - -// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) -float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) -{ - //float ps = 2.0 * (2.0/uv_size.y); // Original -// float ps = debug_psmult * (2.0/uv_size.y); - float ps = (2.0/uv_size.y); -// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now - return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); -} - -float4 mainImage( VertData v_in ) : TARGET { - float2 p = 2.* v_in.uv - 1.; - - float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; - float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; - float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; - float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; - - // Convert from screen coords to potential Quad UV coordinates - float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); - - if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { - return float4(0.0, 0.0, 0.0, 0.0); - } - - float4 texel = image.Sample(textureSampler, uv); - - if ( Antialias_Edges ) { - // Anti-alias edges of texture - texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); - } - return texel; -} - -' +' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { @@ -26531,22 +32041,41 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCrtCurvatureShader { +function Get-OBSPolarShader { -[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] +[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] param( -# Set the strength of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('strength')] +# Set the center_x of OBSPolarShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] [Single] -$Strength, -# Set the border of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('border')] -[String] -$Border, -# Set the feathering of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('feathering')] +$CenterX, +# Set the center_y of OBSPolarShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] [Single] -$Feathering, +$CenterY, +# Set the point_y of OBSPolarShader +[Alias('point_y')] +[ComponentModel.DefaultBindingProperty('point_y')] +[Single] +$PointY, +# Set the flip of OBSPolarShader +[ComponentModel.DefaultBindingProperty('flip')] +[Management.Automation.SwitchParameter] +$Flip, +# Set the rotate of OBSPolarShader +[ComponentModel.DefaultBindingProperty('rotate')] +[Single] +$Rotate, +# Set the repeat of OBSPolarShader +[ComponentModel.DefaultBindingProperty('repeat')] +[Single] +$Repeat, +# Set the scale of OBSPolarShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -26577,50 +32106,84 @@ $UseShaderTime process { -$shaderName = 'crt-curvature' -$ShaderNoun = 'OBSCrtCurvatureShader' +$shaderName = 'polar' +$ShaderNoun = 'OBSPolarShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 200.; - float step = 0.01; -> = 33.33; +#define PI 3.14159265359 +#define PI_2 6.2831 +#define mod(x,y) (x - y * floor(x / y)) -uniform float4 border< - string label = "Border Color"; -> = {0., 0., 0., 1.}; +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; -uniform float feathering< - string label = "Feathering"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 33.33; +uniform float point_y< + string label = "Point y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform bool flip; + +uniform float rotate< + string label = "Rotate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float repeat< + string label = "Repeat"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - float2 cc = v_in.uv - float2(0.5, 0.5); - float dist = dot(cc, cc) * strength / 100.0; - float2 bent = v_in.uv + cc * (1.0 + dist) * dist; - if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { - return border; - } - if (feathering >= .01) { - float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; - float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); - borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; - float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); - return lerp(border, image.Sample(textureSampler, bent), borderFade); + float2 uv = v_in.uv; + uv.x -= center_x ; + uv.y -= center_y ; + uv.x = uv.x * ( uv_size.x / uv_size.y); + float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; + if(repeat < 1.0){ + pixel_angle = mod(pixel_angle+rotate,1.0); + if(pixel_angle > repeat) + return float4(0,0,0,0); + pixel_angle = mod(pixel_angle/repeat,1.0); + } else { + pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); } - - return image.Sample(textureSampler, bent); + float pixel_distance = length(uv)/ scale - point_y; + float2 uv2 = float2(pixel_angle , pixel_distance); + if(flip) + uv2 = float2(1.0,1.0) - uv2; + return image.Sample(textureSampler,uv2); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -26718,37 +32281,62 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCubeRotatingShader { +function Get-OBSPulseShader { -[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] +[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] param( -# Set the images of OBSCubeRotatingShader -[ComponentModel.DefaultBindingProperty('images')] -[Int32] -$Images, -# Set the speed of OBSCubeRotatingShader +# Set the ViewProj of OBSPulseShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSPulseShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSPulseShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSPulseShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSPulseShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSPulseShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSPulseShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSPulseShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed of OBSPulseShader [ComponentModel.DefaultBindingProperty('speed')] [Single] $Speed, -# Set the shadow of OBSCubeRotatingShader -[ComponentModel.DefaultBindingProperty('shadow')] +# Set the min_growth_pixels of OBSPulseShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] [Single] -$Shadow, -# Set the other_image1 of OBSCubeRotatingShader -[Alias('other_image1')] -[ComponentModel.DefaultBindingProperty('other_image1')] -[String] -$OtherImage1, -# Set the other_image2 of OBSCubeRotatingShader -[Alias('other_image2')] -[ComponentModel.DefaultBindingProperty('other_image2')] -[String] -$OtherImage2, -# Set the other_image3 of OBSCubeRotatingShader -[Alias('other_image3')] -[ComponentModel.DefaultBindingProperty('other_image3')] -[String] -$OtherImage3, +$MinGrowthPixels, +# Set the max_growth_pixels of OBSPulseShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -26779,84 +32367,84 @@ $UseShaderTime process { -$shaderName = 'cube_rotating' -$ShaderNoun = 'OBSCubeRotatingShader' +$shaderName = 'pulse' +$ShaderNoun = 'OBSPulseShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int images< - string label = "Images"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "1"; - int option_1_value = 2; - string option_1_label = "2"; - int option_2_value = 4; - string option_2_label = "4"; -> = 1; +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; uniform float speed< string label = "Speed"; string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 0.5; - -uniform float shadow< - string label = "Shadow"; - string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.5; - float step = 0.001; + float maximum = 100.0; + float step = 0.1; > = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 0.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 200.0; -uniform texture2d other_image1; -uniform texture2d other_image2; -uniform texture2d other_image3; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + + float3 pos = v_in.pos.xyz; + float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); + float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; + float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; + + float t = (1 + sin(elapsed_time * speed)) / 2; + float3 current_pos = min_pos * (1 - t) + max_pos * t; + + vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} -#define PI 3.14159265359 float4 mainImage(VertData v_in) : TARGET { - float t = elapsed_time * speed; - float4 c = float4(0,0,0,0); - for(float side = 0.0; side<4.0; side += 1.0){ - float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; - float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; - if(left < right){ - float2 uv; - uv.x = (v_in.uv.x-left)/(right-left); - float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; - float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; - float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); - uv.y = (v_in.uv.y-0.5)*size+0.5; - float4 sample = float4(0,0,0,0); - if(images <= 1 || side == 0.0){ - sample = image.Sample(textureSampler, uv); - }else if(images == 2){ - if(side == 1.0 || side == 3.0){ - sample = other_image1.Sample(textureSampler, uv); - }else{ - sample = image.Sample(textureSampler, uv); - } - }else if(images == 4){ - if(side == 1.0){ - sample = other_image1.Sample(textureSampler, uv); - }else if(side == 2.0){ - sample = other_image2.Sample(textureSampler, uv); - }else if(side == 3.0){ - sample = other_image3.Sample(textureSampler, uv); - }else{ - sample = image.Sample(textureSampler, uv); - } - } - if(sample.a > 0.0){ - c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); - } - } - } - return c; + return image.Sample(textureSampler, v_in.uv); } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -26954,23 +32542,50 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurveShader { +function Get-OBSQuadrilateralCropShader { -[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] +[Alias('Set-OBSQuadrilateralCropShader','Add-OBSQuadrilateralCropShader')] param( -# Set the strength of OBSCurveShader -[ComponentModel.DefaultBindingProperty('strength')] +# Set the Top_Left_X of OBSQuadrilateralCropShader +[Alias('Top_Left_X')] +[ComponentModel.DefaultBindingProperty('Top_Left_X')] [Single] -$Strength, -# Set the scale of OBSCurveShader -[ComponentModel.DefaultBindingProperty('scale')] +$TopLeftX, +# Set the Top_Left_Y of OBSQuadrilateralCropShader +[Alias('Top_Left_Y')] +[ComponentModel.DefaultBindingProperty('Top_Left_Y')] [Single] -$Scale, -# Set the curve_color of OBSCurveShader -[Alias('curve_color')] -[ComponentModel.DefaultBindingProperty('curve_color')] -[String] -$CurveColor, +$TopLeftY, +# Set the Top_Right_X of OBSQuadrilateralCropShader +[Alias('Top_Right_X')] +[ComponentModel.DefaultBindingProperty('Top_Right_X')] +[Single] +$TopRightX, +# Set the Top_Right_Y of OBSQuadrilateralCropShader +[Alias('Top_Right_Y')] +[ComponentModel.DefaultBindingProperty('Top_Right_Y')] +[Single] +$TopRightY, +# Set the Bottom_Left_X of OBSQuadrilateralCropShader +[Alias('Bottom_Left_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] +[Single] +$BottomLeftX, +# Set the Bottom_Left_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Left_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +[Single] +$BottomLeftY, +# Set the Bottom_Right_X of OBSQuadrilateralCropShader +[Alias('Bottom_Right_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +[Single] +$BottomRightX, +# Set the Bottom_Right_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Right_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +[Single] +$BottomRightY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27001,51 +32616,84 @@ $UseShaderTime process { -$shaderName = 'curve' -$ShaderNoun = 'OBSCurveShader' +$shaderName = 'quadrilateral_crop' +$ShaderNoun = 'OBSQuadrilateralCropShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 +// Quadrilateral Crop shader (inverse of a corner pin): transform a 4 points polygon to the corners of the source. +// Useful to revert perspective. -uniform float strength< - string label = "Strength"; +uniform float Top_Left_X< + string label = "Top Left X"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -uniform float scale< - string label = "Scale"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Left_Y< + string label = "Top Left Y"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Right_X< + string label = "Top Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Top_Right_Y< + string label = "Top Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_X< + string label = "Bottom Left X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_Y< + string label = "Bottom Left Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; -uniform float4 curve_color = {0,0,0,0}; +float4 mainImage( VertData v_in ) : TARGET { + + float2 tl = float2(Top_Left_X, Top_Left_Y) * .01; + float2 tr = float2(Top_Right_X, Top_Right_Y) * .01; + float2 bl = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 br = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + float2 t = lerp(tl, tr, v_in.uv[0]); + float2 b = lerp(bl, br, v_in.uv[0]); + float2 uv = lerp(t, b, v_in.uv[1]); -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - const float ydiff = 1.0; - uv -= float2(0.5,ydiff); - uv.x *= ( uv_size.x / uv_size.y); - uv /= scale; - if(strength > 0.0){ - float d = tan((1.0-strength)*PI/2.0); - float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); - float2 center = float2(0.0,(1.0-ydiff)+d); - float pd = distance(uv, center); - if(pd < r) - return curve_color; - float angle = atan2(center.x-uv.x,center.y-uv.y); - uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); - } - uv.x /= ( uv_size.x / uv_size.y); - uv += float2(0.5,ydiff); - return image.Sample(textureSampler,uv); + return image.Sample(textureSampler, uv); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -27143,55 +32791,68 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCutRectPerCornerShader { +function Get-OBSRainbowShader { -[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] +[Alias('Set-OBSRainbowShader','Add-OBSRainbowShader')] param( -# Set the corner_tl of OBSCutRectPerCornerShader -[Alias('corner_tl')] -[ComponentModel.DefaultBindingProperty('corner_tl')] -[Int32] -$CornerTl, -# Set the corner_tr of OBSCutRectPerCornerShader -[Alias('corner_tr')] -[ComponentModel.DefaultBindingProperty('corner_tr')] -[Int32] -$CornerTr, -# Set the corner_br of OBSCutRectPerCornerShader -[Alias('corner_br')] -[ComponentModel.DefaultBindingProperty('corner_br')] -[Int32] -$CornerBr, -# Set the corner_bl of OBSCutRectPerCornerShader -[Alias('corner_bl')] -[ComponentModel.DefaultBindingProperty('corner_bl')] -[Int32] -$CornerBl, -# Set the border_thickness of OBSCutRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSCutRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSCutRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] +# Set the Saturation of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Saturation')] [Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSCutRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] +$Saturation, +# Set the Luminosity of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Luminosity')] [Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSCutRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +$Luminosity, +# Set the Spread of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Spread')] [Single] -$AlphaCutOff, +$Spread, +# Set the Speed of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Alpha_Percentage of OBSRainbowShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Vertical of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the Rotational of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Rotational')] +[Management.Automation.SwitchParameter] +$Rotational, +# Set the Rotation_Offset of OBSRainbowShader +[Alias('Rotation_Offset')] +[ComponentModel.DefaultBindingProperty('Rotation_Offset')] +[Single] +$RotationOffset, +# Set the Apply_To_Image of OBSRainbowShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRainbowShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRainbowShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRainbowShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27222,186 +32883,150 @@ $UseShaderTime process { -$shaderName = 'cut_rect_per_corner' -$ShaderNoun = 'OBSCutRectPerCornerShader' +$shaderName = 'rainbow' +$ShaderNoun = 'OBSRainbowShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_tl< - string label = "Corner top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_tr< - string label = "Corner top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_br< - string label = "Corner bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_bl< - string label = "Corner bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border aplha start"; +// Rainbow shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 13, 2022 +uniform float Saturation< + string label = "Saturation"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border aplha start"; + float step = 0.001; +> = 0.8; // +uniform float Luminosity< + string label = "Luminosity"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; + float step = 0.001; +> = 0.5; // +uniform float Spread< + string label = "Spread"; + string widget_type = "slider"; + float minimum = 0.5; + float maximum = 10.0; float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; +> = 3.8; // +uniform float Speed< + string label = "Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; + float minimum = -10.0; + float maximum = 10.0; float step = 0.01; -> = 0.5; +> = 2.4; // +uniform float Alpha_Percentage< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; // +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 6.28318531; + float step = 0.001; +> = 0.0; // +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; + +float hueToRGB(float v1, float v2, float vH) { + vH = frac(vH); + if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); + if ((2.0 * vH) < 1.0) return (v2); + if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); + return clamp(v1, 0.0, 1.0); +} + +float4 HSLtoRGB(float4 hsl) { + float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); + float v1 = 0.0; + float v2 = 0.0; + + if (hsl.y == 0) { + rgb.xyz = hsl.zzz; + } + else { + + if (hsl.z < 0.5) { + v2 = hsl.z * (1 + hsl.y); + } + else { + v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + } + + v1 = 2.0 * hsl.z - v2; + + rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); + rgb.y = hueToRGB(v1, v2, hsl.x); + rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); + + } + + return rgb; +} float4 mainImage(VertData v_in) : TARGET { - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; - int corner_right = corner_tr>corner_br?corner_tr:corner_br; - int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; - int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = closedEdgeXabs+closedEdgeYabs; - if(d>corner_radius){ - if(d-corner_radius <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } + float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); + + //set colors and direction + float hue = (-1 * lPos.x) / 2.0; + + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + hue = (lPos.x * cosine + lPos.y * sine) * 0.5; + } + + if (Vertical && (Rotational == false)) + { + hue = (-1 * lPos.y) * 0.5; + } + + hue += time; + hue = frac(hue); + float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); + float4 rgba = HSLtoRGB(hsl); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; + float4 luma_color = float4(luma, luma, luma, luma); + if (Replace_Image_Color) + color = luma_color; + rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); } - return float4(0.0,0.0,0.0,0.0); + return rgba; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -27499,20 +33124,36 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCylinderShader { +function Get-OBSRainWindowShader { -[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] +[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] param( -# Set the cylinder_factor of OBSCylinderShader -[Alias('cylinder_factor')] -[ComponentModel.DefaultBindingProperty('cylinder_factor')] +# Set the size of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('size')] [Single] -$CylinderFactor, -# Set the background_cut of OBSCylinderShader -[Alias('background_cut')] -[ComponentModel.DefaultBindingProperty('background_cut')] +$Size, +# Set the blurSize of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('blurSize')] [Single] -$BackgroundCut, +$BlurSize, +# Set the trail_strength of OBSRainWindowShader +[Alias('trail_strength')] +[ComponentModel.DefaultBindingProperty('trail_strength')] +[Single] +$TrailStrength, +# Set the trail_color of OBSRainWindowShader +[Alias('trail_color')] +[ComponentModel.DefaultBindingProperty('trail_color')] +[Single] +$TrailColor, +# Set the speed of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the debug of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('debug')] +[Management.Automation.SwitchParameter] +$DebugShader, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27543,58 +33184,246 @@ $UseShaderTime process { -$shaderName = 'cylinder' -$ShaderNoun = 'OBSCylinderShader' +$shaderName = 'rain-window' +$ShaderNoun = 'OBSRainWindowShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float cylinder_factor< - string label = "Cylinder factor"; +// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro +// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 +// https://www.shadertoy.com/view/ltffzl +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + +uniform float size< + string label = "Rain Drop Size"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; + float minimum = 0.001; + float maximum = 0.5; + float step = 0.01; > = 0.2; -uniform float background_cut< - string label = "Background cut"; +uniform float blurSize< + string label = "Blur Radius"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.1; + float maximum = 100.0; + float step = 0.01; +> = 32.0; // BLUR SIZE (Radius) +uniform float trail_strength< + string label = "Trail Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform float trail_color< + string label = "Trail Color"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 40.0; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.01; +> = 100.0; +uniform bool debug = false; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.x -= 0.5; - float bend = sqrt(1.0 - uv.x*uv.x*4); - uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - uv.x += 0.5; - float4 front_color = image.Sample(textureSampler, uv); - front_color.rgb *= bend/2+0.5; - if(front_color.a >= 1.0) - return front_color; - - uv = v_in.uv; - uv.x -= 0.5; - if(abs(uv.x) < background_cut) - return front_color; - uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - if(uv.x > 0){ - uv.x = 1.0 - uv.x; - }else{ - uv.x = 0 - uv.x; - } - float4 back_color = image.Sample(textureSampler, uv); - back_color.rgb *= 0.5-bend/2; - front_color.rgb *= front_color.a; - front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; - front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; - return front_color; +float fract(float v){ + return v - floor(v); +} + +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); +} + +float3 fract3(float3 v){ + return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); +} + +float3 fract4(float4 v){ + return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); +} + + +float3 N13(float p) { + // from DAVE HOSKINS + float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yzx + 19.19); + return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); +} + +float4 N14(float t) { + return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); +} +float N(float t) { + return fract(sin(t*12345.564)*7658.76); +} + +float Saw(float b, float t) { + return smoothstep(0., b, t)*smoothstep(1., b, t); +} + +float2 Drops(float2 uv, float t) { + + float2 UV = uv; + + // DEFINE GRID + uv.y += t*0.8; + float2 a = float2(6., 1.); + float2 grid = a*2.; + float2 id = floor(uv*grid); + + // RANDOM SHIFT Y + float colShift = N(id.x); + uv.y += colShift; + + // DEFINE SPACES + id = floor(uv*grid); + float3 n = N13(id.x*35.2+id.y*2376.1); + float2 st = fract2(uv*grid)-float2(.5, 0); + + // POSITION DROPS + //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 + float x = n.x-.5; + + float y = UV.y*20.; + + float distort = sin(y+sin(y)); + x += distort*(.5-abs(x))*(n.z-.5); + x *= .7; + float ti = fract(t+n.z); + y = (Saw(.85, ti)-.5)*.9+.5; + float2 p = float2(x, y); + + // DROPS + float d = length((st-p)*a.yx); + + float dSize = size; + + float Drop = smoothstep(dSize, .0, d); + + + float r = sqrt(smoothstep(1., y, st.y)); + float cd = abs(st.x-x); + + // TRAILS + float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); + float trailFront = smoothstep(-.02, .02, st.y-y); + trail *= trailFront; + + + // DROPLETS + y = UV.y; + y += N(id.x); + float trail2 = smoothstep(dSize*r, .0, cd); + float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; + y = fract(y*10.)+(st.y-.5); + float dd = length(st-float2(x, y)); + droplets = smoothstep(dSize*N(id.x), 0., dd); + float m = Drop+droplets*r*trailFront; + if(debug){ + m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES + } + + + return float2(m, trail); +} + +float StaticDrops(float2 uv, float t) { + uv *= 30.; + + float2 id = floor(uv); + uv = fract2(uv)-.5; + float3 n = N13(id.x*107.45+id.y*3543.654); + float2 p = (n.xy-.5)*0.5; + float d = length(uv-p); + + float fade = Saw(.025, fract(t+n.z)); + float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; + + return c; +} + +float2 Rain(float2 uv, float t) { + //float s = StaticDrops(uv, t); + float2 r1 = Drops(uv, t); + float2 r2 = Drops(uv*1.8, t); + float c; + if(debug){ + c = r1.x; + }else{ + c = r1.x+r2.x;//s+r1.x+r2.x; + } + + c = smoothstep(.3, 1., c); + + if(debug){ + return float2(c, r1.y); + }else{ + return float2(c, max(r1.y, r2.y)); + } +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; + uv.y = 1.0 - uv.y; + uv = uv * uv_scale; + float2 UV = v_in.uv; + float T = elapsed_time * speed / 100.0; + + float t = T*.2; + + UV = (UV-.5)*(.9)+.5; + + float2 c = Rain(uv, t); + + float2 e = float2(.001, 0.); //pixel offset + float cx = Rain(uv+e, t).x; + float cy = Rain(uv+e.yx, t).x; + float2 n = float2(cx-c.x, cy-c.x); //normals + + // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj + float Pi = 6.28318530718; // Pi*2 + + // GAUSSIAN BLUR SETTINGS {{{ + float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) + // GAUSSIAN BLUR SETTINGS }}} + float2 Radius = blurSize/uv_size; + float3 col = image.Sample(textureSampler, UV).rgb; + + if(blurSize > 0.0){ + // Blur calculations + for(float d=0.0; d; +uniform int shadow_offset_y< + string label = "shadow offset y"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 1; + +uniform float4 shadow_color; float4 mainImage(VertData v_in) : TARGET { - float4 other = float4(1.0, 1.0, 1.0, 1.0); - float4 base = image.Sample(textureSampler, v_in.uv); - float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); - float4 gray = float4(luminance,luminance,luminance, 1.0); - - return min(base,other); + int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); + float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); + float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); + + float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); + float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); + float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); + + return final_shadow_color * (1-color.a) + color; } ' @@ -27856,20 +33723,28 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDeadPixelFixerShader { +function Get-OBSReflectShader { -[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] +[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] param( -# Set the Dead_Pixel_X of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_X')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] +# Set the Horizontal of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the Vertical of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the center_x_percent of OBSReflectShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] [Int32] -$DeadPixelX, -# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_Y')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] +$CenterXPercent, +# Set the center_y_percent of OBSReflectShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] [Int32] -$DeadPixelY, +$CenterYPercent, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27900,65 +33775,63 @@ $UseShaderTime process { -$shaderName = 'dead-pixel-fixer' -$ShaderNoun = 'OBSDeadPixelFixerShader' +$shaderName = 'Reflect' +$ShaderNoun = 'OBSReflectShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. -// The pixel located at the user configured offset will have its color overridden by taking the average of the -// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. -// -// Changelog: -// 0.01 - Initial release +// Simple Reflect Shader -uniform int Dead_Pixel_X< - string label = "Dead Pixel X"; +// Reflects horizontally and/or vertically. + +uniform bool Horizontal< + string label = "Reflect horizontally"; +> = false; +uniform bool Vertical< + string label = "Reflect vertically"; +> = true; + +uniform int center_x_percent< + string label = "center x percentage"; string widget_type = "slider"; int minimum = 0; - int maximum = 2000; + int maximum = 100; int step = 1; -> = 0; -uniform int Dead_Pixel_Y< - string label = "Dead Pixel Y"; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; string widget_type = "slider"; int minimum = 0; - int maximum = 2000; + int maximum = 100; int step = 1; -> = 0; +> = 50; -float3 blur_dead_pixel(in float2 pos) -{ - float3 color; - color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; - return color * 0.25; -} -float4 mainImage( VertData v_in ) : TARGET +float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - float2 pos = v_in.pos.xy; - float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); - float4 obstex = image.Sample(textureSampler, uv); - float3 color; - - if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { - color = blur_dead_pixel(pos); - } else { - color.rgb = obstex.rgb; - } - return float4( color, obstex.a); + float2 pos = v_in.uv; + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + + if (Horizontal == true) { + if (pos.x < center_pos.x) { + pos.x = center_pos.x - pos.x; + } else if (pos.x == center_pos.x) { + pos.x = pos.x; + } else { + pos.x = pos.x - center_pos.x; + } + } + if (Vertical == true) { + if (pos.y < center_pos.y) { + pos.y = center_pos.y - pos.y; + } else if (pos.y == center_pos.y) { + pos.y = pos.y; + } else { + pos.y = pos.y - center_pos.y; + } + } + + return image.Sample(textureSampler, pos); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -28056,114 +33929,19 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDensitySatHueShader { +function Get-OBSRemovePartialPixelsShader { -[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] +[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] param( -# Set the notes of OBSDensitySatHueShader +# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the notes of OBSRemovePartialPixelsShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, -# Set the density_r of OBSDensitySatHueShader -[Alias('density_r')] -[ComponentModel.DefaultBindingProperty('density_r')] -[Single] -$DensityR, -# Set the saturation_r of OBSDensitySatHueShader -[Alias('saturation_r')] -[ComponentModel.DefaultBindingProperty('saturation_r')] -[Single] -$SaturationR, -# Set the hueShift_r of OBSDensitySatHueShader -[Alias('hueShift_r')] -[ComponentModel.DefaultBindingProperty('hueShift_r')] -[Single] -$HueShiftR, -# Set the density_y of OBSDensitySatHueShader -[Alias('density_y')] -[ComponentModel.DefaultBindingProperty('density_y')] -[Single] -$DensityY, -# Set the saturation_y of OBSDensitySatHueShader -[Alias('saturation_y')] -[ComponentModel.DefaultBindingProperty('saturation_y')] -[Single] -$SaturationY, -# Set the hueShift_y of OBSDensitySatHueShader -[Alias('hueShift_y')] -[ComponentModel.DefaultBindingProperty('hueShift_y')] -[Single] -$HueShiftY, -# Set the density_g of OBSDensitySatHueShader -[Alias('density_g')] -[ComponentModel.DefaultBindingProperty('density_g')] -[Single] -$DensityG, -# Set the saturation_g of OBSDensitySatHueShader -[Alias('saturation_g')] -[ComponentModel.DefaultBindingProperty('saturation_g')] -[Single] -$SaturationG, -# Set the hueShift_g of OBSDensitySatHueShader -[Alias('hueShift_g')] -[ComponentModel.DefaultBindingProperty('hueShift_g')] -[Single] -$HueShiftG, -# Set the density_c of OBSDensitySatHueShader -[Alias('density_c')] -[ComponentModel.DefaultBindingProperty('density_c')] -[Single] -$DensityC, -# Set the saturation_c of OBSDensitySatHueShader -[Alias('saturation_c')] -[ComponentModel.DefaultBindingProperty('saturation_c')] -[Single] -$SaturationC, -# Set the hueShift_c of OBSDensitySatHueShader -[Alias('hueShift_c')] -[ComponentModel.DefaultBindingProperty('hueShift_c')] -[Single] -$HueShiftC, -# Set the density_b of OBSDensitySatHueShader -[Alias('density_b')] -[ComponentModel.DefaultBindingProperty('density_b')] -[Single] -$DensityB, -# Set the saturation_b of OBSDensitySatHueShader -[Alias('saturation_b')] -[ComponentModel.DefaultBindingProperty('saturation_b')] -[Single] -$SaturationB, -# Set the hueShift_b of OBSDensitySatHueShader -[Alias('hueShift_b')] -[ComponentModel.DefaultBindingProperty('hueShift_b')] -[Single] -$HueShiftB, -# Set the density_m of OBSDensitySatHueShader -[Alias('density_m')] -[ComponentModel.DefaultBindingProperty('density_m')] -[Single] -$DensityM, -# Set the saturation_m of OBSDensitySatHueShader -[Alias('saturation_m')] -[ComponentModel.DefaultBindingProperty('saturation_m')] -[Single] -$SaturationM, -# Set the hueShift_m of OBSDensitySatHueShader -[Alias('hueShift_m')] -[ComponentModel.DefaultBindingProperty('hueShift_m')] -[Single] -$HueShiftM, -# Set the global_density of OBSDensitySatHueShader -[Alias('global_density')] -[ComponentModel.DefaultBindingProperty('global_density')] -[Single] -$GlobalDensity, -# Set the global_saturation of OBSDensitySatHueShader -[Alias('global_saturation')] -[ComponentModel.DefaultBindingProperty('global_saturation')] -[Single] -$GlobalSaturation, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28194,261 +33972,37 @@ $UseShaderTime process { -$shaderName = 'density_sat_hue' -$ShaderNoun = 'OBSDensitySatHueShader' +$shaderName = 'remove_partial_pixels' +$ShaderNoun = 'OBSRemovePartialPixelsShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Density-Saturation-Hue Shader for OBS Shaderfilter -// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 - +// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int minimum_alpha_percent< + string label = "minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; uniform string notes< string widget_type = "info"; -> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; - -uniform float density_r < - string label = "Red Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_r < - string label = "Red Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_r < - string label = "Red Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float density_y < - string label = "Yellow Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_y < - string label = "Yellow Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_y < - string label = "Yellow Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float density_g < - string label = "Green Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_g < - string label = "Green Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_g < - string label = "Green Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float density_c < - string label = "Cyan Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_c < - string label = "Cyan Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_c < - string label = "Cyan Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float density_b < - string label = "Blue Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_b < - string label = "Blue Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_b < - string label = "Blue Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float density_m < - string label = "Magenta Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_m < - string label = "Magenta Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float hueShift_m < - string label = "Magenta Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float global_density < - string label = "Global Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float global_saturation < - string label = "Global Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; -// Tetrahedral interpolation based on the ordering of the input color channels -float3 applyAdjustments(float3 p) { - // Pre-calculate the flipped density values for each channel: - float d_r = -(density_r + global_density); - float d_y = -(density_y + global_density); - float d_g = -(density_g + global_density); - float d_c = -(density_c + global_density); - float d_b = -(density_b + global_density); - float d_m = -(density_m + global_density); - - // Compute the color vectors for each hue region using the flipped densities: - float3 red = float3( - 1.0 + d_r, - d_r - (saturation_r + global_saturation), - d_r + hueShift_r - (saturation_r + global_saturation) - ); - - float3 yellow = float3( - 1.0 + hueShift_y + d_y, - 1.0 + d_y, - d_y - (saturation_y + global_saturation) - ); - - float3 green = float3( - d_g - (saturation_g + global_saturation), - 1.0 + d_g, - d_g + hueShift_g - (saturation_g + global_saturation) - ); - - float3 cyan = float3( - d_c - (saturation_c + global_saturation), - 1.0 + hueShift_c + d_c, - 1.0 + d_c - ); - - float3 blue = float3( - d_b + hueShift_b - (saturation_b + global_saturation), - d_b - (saturation_b + global_saturation), - 1.0 + d_b - ); - - float3 magenta = float3( - 1.0 + d_m, - d_m - (saturation_m + global_saturation), - 1.0 + hueShift_m + d_m - ); - - // Define the black and white endpoints: - float3 blk = float3(0.0, 0.0, 0.0); - float3 wht = float3(1.0, 1.0, 1.0); - - float3 rgb; - if (p.r > p.g) { - if (p.g > p.b) { - // p.r >= p.g >= p.b - rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); - } else if (p.r > p.b) { - // p.r >= p.b > p.g - rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); - } else { - // p.b >= p.r > p.g - rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; - } - } else { - if (p.b > p.g) { - // p.b >= p.g >= p.r - rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; - } else if (p.b > p.r) { - // p.g >= p.r and p.b > p.r - rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); - } else { - // p.g >= p.b >= p.r - rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); - } +float4 mainImage(VertData v_in) : TARGET +{ + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + else + { + return float4(output_color); } - return clamp(rgb, 0.0, 1.0); -} - -float4 mainImage(VertData v_in) : TARGET { - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float3 adjustedColor = applyAdjustments(inputColor.rgb); - return float4(adjustedColor, inputColor.a); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -28546,35 +34100,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDiffuseTransitionShader { +function Get-OBSRepeatGridCenterCropShader { -[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] +[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] param( -# Set the image_a of OBSDiffuseTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSDiffuseTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] +# Set the ViewProj of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$ImageB, -# Set the transition_time of OBSDiffuseTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] +$Image, +# Set the alpha of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('alpha')] [Single] -$TransitionTime, -# Set the convert_linear of OBSDiffuseTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the num_samples of OBSDiffuseTransitionShader -[Alias('num_samples')] -[ComponentModel.DefaultBindingProperty('num_samples')] -[Int32] -$NumSamples, +$Alpha, +# Set the columns of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('columns')] +[Single] +$Columns, +# Set the rows of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('rows')] +[Single] +$Rows, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28605,94 +34154,61 @@ $UseShaderTime process { -$shaderName = 'diffuse_transition' -$ShaderNoun = 'OBSDiffuseTransitionShader' +$shaderName = 'repeat_grid_center_crop' +$ShaderNoun = 'OBSRepeatGridCenterCropShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/4cK3z1 -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -// Diffuse (move pixels) between two 2D images -// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) -// Note: the approach in this demo is different - rather than randomising paths we use means - -// increase for greater precision - this is O(n^2) :( -uniform int num_samples< - string label = "Number of samples (10)"; - string widget_type = "slider"; - int minimum = 2; - int maximum = 100; - int step = 1; -> = 10; - -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; +// repeat_grid_center_crop.effect - if (transition_time < 0.00001) { - return image_a.Sample(textureSampler, uv); - } - - // we need to normalise the distributions so just sum the samples for a division later - // note: could calculate this once per image in a buffer or something - float3 from_total = float3(0.0,0.0,0.0); - float3 to_total = float3(0.0,0.0,0.0); +uniform float4x4 ViewProj; +uniform texture2d image; +sampler_state def_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - for (int i=0; i = 1.0; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; -uniform float char_speed< - string label = "Character Speed"; +uniform float alpha< + string label = "Alpha"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.0; - float step = .001; + float maximum = 3.0; + float step = 0.001; > = 1.0; - -uniform float glow_contrast< - string label = "Glow contrast"; +uniform float copies< + string label = "Copies"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.5; - float step = .001; -> = 1.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; +uniform string notes< + string widget_type = "info"; +> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; +sampler_state def_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; -float mod(float x, float y) -{ - return x - y * floor(x/y); -} +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -float2 mod2(float2 x, float2 y) +VertInOut VSDefault(VertInOut vert_in) { - return x - y * floor(x/y); + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv = vert_in.uv * sqrt(copies); + return vert_out; } -float text(float2 fragCoord) +float4 PSDrawBare(VertInOut vert_in) : TARGET { - float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; - float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; - uv = uv*.8+.1; // scale the letters up a bit - block += elapsed_time*.002*char_speed; - uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters - uv *= .0625; // bring back into 0-1 range - return font.Sample(textureSampler, uv).r; + float4 rgba = image.Sample(def_sampler, vert_in.uv); + rgba.a *= alpha; + return rgba; } -float3 rain(float2 fragCoord) +technique Draw { - fragCoord.x -= mod(fragCoord.x, 16.); - float offset=sin(fragCoord.x*15.); - float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; - - float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); - return base_color.rgb / pow(y*20.0, glow_contrast); + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } } -float4 mainImage(VertData v_in) : TARGET -{ - return mix(image.Sample(textureSampler, v_in.uv),float4(rain(float2(v_in.uv.x,1.0-v_in.uv.y)*uv_size),1.0), text(v_in.uv*uv_size)); -} + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29024,33 +34575,85 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDivideRotateShader { +function Get-OBSRepeatTextureShader { -[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] +[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] param( -# Set the iChannel0 of OBSDivideRotateShader -[ComponentModel.DefaultBindingProperty('iChannel0')] +# Set the ViewProj of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the color_matrix of OBSRepeatTextureShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color_range_min of OBSRepeatTextureShader +[Alias('color_range_min')] +[ComponentModel.DefaultBindingProperty('color_range_min')] +[Single[]] +$ColorRangeMin, +# Set the color_range_max of OBSRepeatTextureShader +[Alias('color_range_max')] +[ComponentModel.DefaultBindingProperty('color_range_max')] +[Single[]] +$ColorRangeMax, +# Set the image of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$IChannel0, -# Set the speed_percentage of OBSDivideRotateShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSDivideRotateShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSDivideRotateShader +$Image, +# Set the tex_image of OBSRepeatTextureShader +[Alias('tex_image')] +[ComponentModel.DefaultBindingProperty('tex_image')] +[String] +$TexImage, +# Set the elapsed_time of OBSRepeatTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRepeatTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRepeatTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRepeatTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSRepeatTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSRepeatTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the blend of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('blend')] +[Single] +$Blend, +# Set the copies of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('copies')] +[Single] +$Copies, +# Set the notes of OBSRepeatTextureShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, +# Set the alpha_percentage of OBSRepeatTextureShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Single] +$AlphaPercentage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29081,90 +34684,102 @@ $UseShaderTime process { -$shaderName = 'divide_rotate' -$ShaderNoun = 'OBSDivideRotateShader' +$shaderName = 'repeat_texture' +$ShaderNoun = 'OBSRepeatTextureShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// divide and rotate shader for OBS Studio shaderfilter plugin -// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) -// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform texture2d iChannel0; -uniform int speed_percentage< - string label = "Speed"; +// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 + +uniform float4x4 ViewProj; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; +uniform texture2d image; +uniform texture2d tex_image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; + +uniform float blend< + string label = "Blend"; string widget_type = "slider"; - int minimum = -10; - int maximum = 10; - int step = 1; -> = 5; -uniform int alpha_percentage< - string label = "Opacity Percentage"; + float minimum = 0.0; + float maximum = 3.0; + float step = 0.001; +> = 1.0; +uniform float copies< + string label = "Copies"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = true; - + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; uniform string notes< string widget_type = "info"; -> = "add rotation and speed"; - - -float2 cm(float2 a, float2 b) { - return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); -} - -float2 iter(float2 uv, float2 rot, float scale) { - float2 gv = frac(cm(uv, rot) * scale); - float boundDist = 1. - max(abs(gv.x), abs(gv.y)); - float mask = step(.03, boundDist); - gv *= mask; - return gv; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); - float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); - - // Normalize coords - //float2 uv = (v_in.uv * uv_scale + uv_offset); - float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; - float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; +> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; +uniform float alpha_percentage< + string label = "alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; - // Add some time rotation and offset - float t = elapsed_time * speed; - float2 time = float2(sin(t), cos(t)); - uv += time; +sampler_state tex_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; - // Imaginary component has to be mirrored for natural feeling rotation - mouse.y *= -1.0; +sampler_state base_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - // Draw few layers of this to bend space - float2 rot = cm(mouse, time); - for (float i=1.0; i<=3.0; i++) { - uv = iter(uv, rot, 1.5); - } +struct VertIn { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; - // Combine background with new image - float4 background_color = image.Sample(textureSampler, v_in.uv); - float4 col = iChannel0.Sample(textureSampler, uv); +struct VertOut { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; - // Border - if (uv.x == 0.0 && uv.y == 0.0) { - col = float4(0,0,0,alpha); - } +VertOut VSDefault(VertIn vert_in) +{ + VertOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv_1 = vert_in.uv_0; + vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); + return vert_out; +} - // if not appling to alpha layer, set output alpha - if (Apply_To_Alpha_Layer == false) - col.a = alpha; +float4 PSDrawBare(VertOut vert_in) : TARGET +{ + float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); + float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); + float4 base = image.Sample(base_sampler, vert_in.uv_1); - //output color is combined with background image - col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); + return (1 - alpha) * base + (alpha) * tex; +} - return col; +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29262,62 +34877,26 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDoodleShader { +function Get-OBSRGBAPercentShader { -[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] +[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] param( -# Set the ViewProj of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSDoodleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the RedPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('RedPercent')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSDoodleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSDoodleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSDoodleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSDoodleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$RedPercent, +# Set the GreenPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('GreenPercent')] [Single] -$RandF, -# Set the uv_size of OBSDoodleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the Doodle_Scale_Percent of OBSDoodleShader -[Alias('Doodle_Scale_Percent')] -[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] +$GreenPercent, +# Set the BluePercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('BluePercent')] [Single] -$DoodleScalePercent, -# Set the Snap_Percent of OBSDoodleShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] +$BluePercent, +# Set the AlphaPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('AlphaPercent')] [Single] -$SnapPercent, -# Set the Notes of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, +$AlphaPercent, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29348,94 +34927,58 @@ $UseShaderTime process { -$shaderName = 'doodle' -$ShaderNoun = 'OBSDoodleShader' +$shaderName = 'RGBA_Percent' +$ShaderNoun = 'OBSRGBAPercentShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -uniform float4x4 ViewProj; -uniform texture2d image; +// Simple RGBA Percent Shader +// Allows Red, Green, or Blue to be adjusted between 0-200% +// Allows Alpha to be adjusted between 0/100% +uniform float RedPercent< + string label = "Red percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; +uniform float GreenPercent< + string label = "Green percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; -uniform float Doodle_Scale_Percent< - string label = "Doodle Scale Percent"; +uniform float BluePercent< + string label = "Blue percentage"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 2.5; -uniform float Snap_Percent< - string label = "Snap Percent"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 0.1; -> = 7.5; -uniform string Notes< - string widget_type = "info"; -> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + float maximum = 200; + float step = 1.0; +> = 100.0; -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -float3 rand3(float3 co) -{ - float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = frac(512.0*j); - j *= .125; - result.x = frac(512.0*j); - j *= .125; - result.y = frac(512.0*j); - return result - 0.5; -} +uniform float AlphaPercent< + string label = "Alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; -float snap(float x, float snap) +float4 mainImage(VertData v_in) : TARGET { - return snap * round(x / max(0.01,snap)); -} - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); - float rand = snap(rand_f, Snap_Percent *.01); - float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); - vert_out.uv.xy += noise; - - return vert_out; -} - -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} - -technique Draw -{ - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } + float2 pos = v_in.uv; + + float4 imageColors = image.Sample(textureSampler, v_in.uv); + float4 adjustedColor = float4( + imageColors.r * (RedPercent * 0.01), + imageColors.g * (GreenPercent * 0.01), + imageColors.b * (BluePercent * 0.01), + imageColors.a * (AlphaPercent * 0.01) + ); + return adjustedColor; } ' @@ -29535,18 +35078,54 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrawingsShader { +function Get-OBSRgbColorWheelShader { -[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] +[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] param( -# Set the AngleNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('AngleNum')] +# Set the speed of OBSRgbColorWheelShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the color_depth of OBSRgbColorWheelShader +[Alias('color_depth')] +[ComponentModel.DefaultBindingProperty('color_depth')] +[Single] +$ColorDepth, +# Set the Apply_To_Image of OBSRgbColorWheelShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRgbColorWheelShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRgbColorWheelShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Alpha_Percentage of OBSRgbColorWheelShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the center_width_percentage of OBSRgbColorWheelShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] [Int32] -$AngleNum, -# Set the SampNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('SampNum')] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRgbColorWheelShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] [Int32] -$SampNum, +$CenterHeightPercentage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29577,143 +35156,100 @@ $UseShaderTime process { -$shaderName = 'drawings' -$ShaderNoun = 'OBSDrawingsShader' +$shaderName = 'rgb_color_wheel' +$ShaderNoun = 'OBSRgbColorWheelShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldlcWs - -uniform int AngleNum< - string label = "Number of angles"; +// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float speed< + string label = "Speed"; string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; + float minimum = 0.0; + float maximum = 15.0; + float step = 0.1; +> = 2.0; +uniform float color_depth< + string label = "Color Depth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.1; +> = 2.10; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100; // +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; int step = 1; -> = 3; -uniform int SampNum< - string label = "Number of samples"; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; + int minimum = 0; + int maximum = 100; int step = 1; -> = 9; - -float4 getCol(float2 pos) -{ - // take aspect ratio into account - float2 uv=pos; - float4 c1=image.Sample(textureSampler, uv); - float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); - c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); - float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); - float4 c2=float4(.7,.7,.7,.7); - return min(lerp(c1,c2,1.8*d),.7); -} +> = 50; -float4 getColHT(float2 pos) +float3 hsv2rgb(float3 c) { - return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); } -float getVal(float2 pos) +float mod(float x, float y) { - float4 c=getCol(pos); - return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; + return x - y * floor(x / y); } -float2 getGrad(float2 pos, float eps) +float4 mainImage(VertData v_in) : TARGET { - float2 d=float2(eps,0.); - return float2( - getVal(pos+d.xy)-getVal(pos-d.xy), - getVal(pos+d.yx)-getVal(pos-d.yx) - )/eps/2.; -} - - - float lum( float3 c) { - return dot(c, float3(0.3, 0.59, 0.11)); - } - - - float3 clipcolor( float3 c) { - float l = lum(c); - float n = min(min(c.r, c.g), c.b); - float x = max(max(c.r, c.g), c.b); - - if (n < 0.0) { - c.r = l + ((c.r - l) * l) / (l - n); - c.g = l + ((c.g - l) * l) / (l - n); - c.b = l + ((c.b - l) * l) / (l - n); - } - if (x > 1.25) { - c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); - c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); - c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); - } - return c; - } + const float PI = 3.14159265f;//acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st ; + float r = length(toCenter) * color_depth; + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(speed ,18)) / 18; - float3 setlum( float3 c, float l) { - float d = l - lum(c); - c = c + float3(d,d,d); - return clipcolor(0.85*c); - } + rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - float3 col = float3(0,0,0); - float3 col2 = float3(0,0,0); - float sum=0.; - - for(int i=0;i = 5; -uniform int shadow_offset_y< - string label = "Shadow offset y"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 2.00; +uniform float redy< + string label = "Red Y"; string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 5; -uniform int shadow_blur_size< - string label = "Shadow blur size"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greenx< + string label = "Green X"; string widget_type = "slider"; - int minimum = 0; - int maximum = 15; - int step = 1; -> = 3; -uniform string notes< - string widget_type = "info"; -> = "blur size is limited to a max of 15 to ensure GPU"; - -uniform float4 shadow_color; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greeny< + string label = "Green Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float bluex< + string label = "Blue X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = -2.00; +uniform float bluey< + string label = "Blue Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; -uniform bool is_alpha_premultiplied; float4 mainImage(VertData v_in) : TARGET { - int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); - int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); - - float sampled_shadow_alpha = 0.0; - - for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) - { - for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); - } - } - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); - return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); + float4 c = image.Sample(textureSampler, v_in.uv); + if(redx != 0.0 || redy != 0.0) + c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; + if(greenx != 0.0 || greeny != 0.0) + c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; + if(bluex != 0.0 || bluey != 0.0) + c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; + return c; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30032,59 +35562,36 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrunkShader { +function Get-OBSRgbvisibilityShader { -[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] +[Alias('Set-OBSRgbvisibilityShader','Add-OBSRgbvisibilityShader')] param( -# Set the color_matrix of OBSDrunkShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the glow_percent of OBSDrunkShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSDrunkShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSDrunkShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed_percent of OBSDrunkShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the Apply_To_Alpha_Layer of OBSDrunkShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the glow_color of OBSDrunkShader -[Alias('glow_color')] -[ComponentModel.DefaultBindingProperty('glow_color')] -[String] -$GlowColor, -# Set the ease of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the Red of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Red')] +[Single] +$Red, +# Set the Green of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Green')] +[Single] +$Green, +# Set the Blue of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the RedVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('RedVisibility')] +[Single] +$RedVisibility, +# Set the GreenVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('GreenVisibility')] +[Single] +$GreenVisibility, +# Set the BlueVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('BlueVisibility')] +[Single] +$BlueVisibility, +# Set the notes of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, # The name of the source. This must be provided when adding an item for the first time @@ -30117,162 +35624,74 @@ $UseShaderTime process { -$shaderName = 'drunk' -$ShaderNoun = 'OBSDrunkShader' +$shaderName = 'rgbvisibility' +$ShaderNoun = 'OBSRgbvisibilityShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -uniform float4x4 color_matrix; +// RGB visibility separation filter, created by EposVox +uniform float Red< + string label = "Red"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; -uniform int glow_percent< - string label = "Glow percent"; +uniform float Green< + string label = "Green"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; + +uniform float Blue< + string label = "Blue"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; + +uniform float RedVisibility< + string label = "Red Visibility"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float GreenVisibility< + string label = "Green Visibility"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float BlueVisibility< + string label = "Blue Visibility"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; -uniform float4 glow_color; -uniform bool ease; -uniform bool glitch; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + uniform string notes< string widget_type = "info"; -> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; - - -// Gaussian Blur -float Gaussian(float x, float o) { - const float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); -} - - -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} - -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; -} - -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} +> = "Modify Colors to correct for gamma, use equal values for general correction."; float4 mainImage(VertData v_in) : TARGET { - float2 offsets[4]; - offsets[0] = float2(-0.05, 0.066); - offsets[1] = float2(-0.05, -0.066); - offsets[2] = float2(0.05, -0.066); - offsets[3] = float2(0.05, 0.066); - - // convert input for vector math - float blur_amount = float(blur) /100; - float glow_amount = float(glow_percent) * 0.1; - float speed = float(pulse_speed_percent) * 0.01; - float luminance_floor = float(min_brightness) * 0.01; - float luminance_ceiling = float(max_brightness) * 0.01; - - float4 color = image.Sample(textureSampler, v_in.uv); - float4 temp_color = color; - bool glitch_on = glitch; - - //circular easing variable - float t = 1 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - - //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); - float4 glitch_color = glow_color; - - for (int n = 0; n < 4; n++){ - //blur sample - b = BlurStyler(t,0,c,d,ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; - - //test for rand_f color - if (glitch) { - glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); - if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) - { - glitch_on = true; - } - } - - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance - ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color - glitch_on) //test for rand color - { - //glow calc - if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); - //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); - - // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color - // max is used to simulate addition of vector texture color - temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time - max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time - * (glitch_color * (b / 2)); // glow color affected by glow over time - } - } - } - // grab lighter color - return max(color,temp_color); + float4 c = image.Sample(textureSampler, v_in.uv); + float redChannel = pow(c.r, Red) * RedVisibility; + float greenChannel = pow(c.g, Green) * GreenVisibility; + float blueChannel = pow(c.b, Blue) * BlueVisibility; + + return float4(redChannel, greenChannel, blueChannel, c.a); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30370,135 +35789,22 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDynamicMaskShader { +function Get-OBSRGSSAAShader { -[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] +[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] param( -# Set the input_source of OBSDynamicMaskShader -[Alias('input_source')] -[ComponentModel.DefaultBindingProperty('input_source')] -[String] -$InputSource, -# Set the red_base_value of OBSDynamicMaskShader -[Alias('red_base_value')] -[ComponentModel.DefaultBindingProperty('red_base_value')] -[Single] -$RedBaseValue, -# Set the red_red_input_value of OBSDynamicMaskShader -[Alias('red_red_input_value')] -[ComponentModel.DefaultBindingProperty('red_red_input_value')] -[Single] -$RedRedInputValue, -# Set the red_green_input_value of OBSDynamicMaskShader -[Alias('red_green_input_value')] -[ComponentModel.DefaultBindingProperty('red_green_input_value')] -[Single] -$RedGreenInputValue, -# Set the red_blue_input_value of OBSDynamicMaskShader -[Alias('red_blue_input_value')] -[ComponentModel.DefaultBindingProperty('red_blue_input_value')] -[Single] -$RedBlueInputValue, -# Set the red_alpha_input_value of OBSDynamicMaskShader -[Alias('red_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] -[Single] -$RedAlphaInputValue, -# Set the red_multiplier of OBSDynamicMaskShader -[Alias('red_multiplier')] -[ComponentModel.DefaultBindingProperty('red_multiplier')] -[Single] -$RedMultiplier, -# Set the green_base_value of OBSDynamicMaskShader -[Alias('green_base_value')] -[ComponentModel.DefaultBindingProperty('green_base_value')] -[Single] -$GreenBaseValue, -# Set the green_red_input_value of OBSDynamicMaskShader -[Alias('green_red_input_value')] -[ComponentModel.DefaultBindingProperty('green_red_input_value')] -[Single] -$GreenRedInputValue, -# Set the green_green_input_value of OBSDynamicMaskShader -[Alias('green_green_input_value')] -[ComponentModel.DefaultBindingProperty('green_green_input_value')] -[Single] -$GreenGreenInputValue, -# Set the green_blue_input_value of OBSDynamicMaskShader -[Alias('green_blue_input_value')] -[ComponentModel.DefaultBindingProperty('green_blue_input_value')] -[Single] -$GreenBlueInputValue, -# Set the green_alpha_input_value of OBSDynamicMaskShader -[Alias('green_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] -[Single] -$GreenAlphaInputValue, -# Set the green_multiplier of OBSDynamicMaskShader -[Alias('green_multiplier')] -[ComponentModel.DefaultBindingProperty('green_multiplier')] -[Single] -$GreenMultiplier, -# Set the blue_base_value of OBSDynamicMaskShader -[Alias('blue_base_value')] -[ComponentModel.DefaultBindingProperty('blue_base_value')] -[Single] -$BlueBaseValue, -# Set the blue_red_input_value of OBSDynamicMaskShader -[Alias('blue_red_input_value')] -[ComponentModel.DefaultBindingProperty('blue_red_input_value')] -[Single] -$BlueRedInputValue, -# Set the blue_green_input_value of OBSDynamicMaskShader -[Alias('blue_green_input_value')] -[ComponentModel.DefaultBindingProperty('blue_green_input_value')] -[Single] -$BlueGreenInputValue, -# Set the blue_blue_input_value of OBSDynamicMaskShader -[Alias('blue_blue_input_value')] -[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] -[Single] -$BlueBlueInputValue, -# Set the blue_alpha_input_value of OBSDynamicMaskShader -[Alias('blue_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] -[Single] -$BlueAlphaInputValue, -# Set the blue_multiplier of OBSDynamicMaskShader -[Alias('blue_multiplier')] -[ComponentModel.DefaultBindingProperty('blue_multiplier')] -[Single] -$BlueMultiplier, -# Set the alpha_base_value of OBSDynamicMaskShader -[Alias('alpha_base_value')] -[ComponentModel.DefaultBindingProperty('alpha_base_value')] -[Single] -$AlphaBaseValue, -# Set the alpha_red_input_value of OBSDynamicMaskShader -[Alias('alpha_red_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] -[Single] -$AlphaRedInputValue, -# Set the alpha_green_input_value of OBSDynamicMaskShader -[Alias('alpha_green_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] -[Single] -$AlphaGreenInputValue, -# Set the alpha_blue_input_value of OBSDynamicMaskShader -[Alias('alpha_blue_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] -[Single] -$AlphaBlueInputValue, -# Set the alpha_alpha_input_value of OBSDynamicMaskShader -[Alias('alpha_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] +# Set the ColorSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('ColorSigma')] [Single] -$AlphaAlphaInputValue, -# Set the alpha_multiplier of OBSDynamicMaskShader -[Alias('alpha_multiplier')] -[ComponentModel.DefaultBindingProperty('alpha_multiplier')] +$ColorSigma, +# Set the SpatialSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('SpatialSigma')] [Single] -$AlphaMultiplier, +$SpatialSigma, +# Set the notes of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30529,220 +35835,154 @@ $UseShaderTime process { -$shaderName = 'dynamic-mask' -$ShaderNoun = 'OBSDynamicMaskShader' +$shaderName = 'RGSSAA' +$ShaderNoun = 'OBSRGSSAAShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d input_source< - string label = "Input Source"; ->; +// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 +// https://github.com/exeldro/obs-shaderfilter/tree/master +// Using edge detection shader as a base, created by Hallatore +// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 -uniform float red_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float red_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_multiplier< - string label = "Multiplier"; +uniform float ColorSigma< + string label = "Color Sigma"; string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; > = 1.0; -uniform float green_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float green_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_multiplier< - string label = "Multiplier"; +uniform float SpatialSigma< + string label = "Spatial Sigma"; string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; > = 1.0; -uniform float blue_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float blue_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; +uniform string notes< + string widget_type = "info"; +> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." -uniform float alpha_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float alpha_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; +float Luminance(float3 rgb) +{ + return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; +} float4 mainImage(VertData v_in) : TARGET { - float4 input_color = input_source.Sample(textureSampler, v_in.uv); - float4 mask; - mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; - mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; - mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; - mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; - float4 base = image.Sample(textureSampler, v_in.uv); - return base * mask; + float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; + float2 SceneUV = v_in.uv; + float2 TexelScale = 1.0f/uv_size; + + const float SQRT2 = 1.4142135624; + const float PI = 3.141592654; + const float angle = PI / 8.0; + const float cs = cos(angle); + const float sn = sin(angle); + + // Rotated grid samples + float3 C1 = + image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; + float3 C2 = + image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; + float3 C3 = + image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; + float3 C4 = + image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; + float3 C5 = + image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; + float3 C6 = + image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; + float3 C7 = + image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; + float3 C8 = + image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; + + // Luminance edge detection + float A0 = Luminance(SceneColor); + float CL1 = Luminance(C1); + float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); + float CL2 = Luminance(C2); + float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); + float CL3 = Luminance(C3); + float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); + float CL4 = Luminance(C4); + float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); + float CL5 = Luminance(C5); + float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); + float CL6 = Luminance(C6); + float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); + float CL7 = Luminance(C7); + float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); + float CL8 = Luminance(C8); + float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); + float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); + + // Calculate distance-based weights + float2 Dist1 = float2(cs, -sn); + float2 Dist2 = float2(-cs, -sn); + float2 Dist3 = float2(-sn, cs); + float2 Dist4 = float2(sn, cs); + float2 Dist5 = float2(cs * SQRT2, 0); + float2 Dist6 = float2(0, sn * SQRT2); + float2 Dist7 = float2(-cs * SQRT2, 0); + float2 Dist8 = float2(0, -sn * SQRT2); + float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); + float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); + float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); + float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); + float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); + float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); + float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); + float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); + + // Color weights + float3 ColorDiff1 = SceneColor.rgb - C1; + float3 ColorDiff2 = SceneColor.rgb - C2; + float3 ColorDiff3 = SceneColor.rgb - C3; + float3 ColorDiff4 = SceneColor.rgb - C4; + float3 ColorDiff5 = SceneColor.rgb - C5; + float3 ColorDiff6 = SceneColor.rgb - C6; + float3 ColorDiff7 = SceneColor.rgb - C7; + float3 ColorDiff8 = SceneColor.rgb - C8; + + float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); + float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); + float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); + float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); + float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); + float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); + float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); + float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); + + // Mixing weights + float W1 = SW1 * CW1; + float W2 = SW2 * CW2; + float W3 = SW3 * CW3; + float W4 = SW4 * CW4; + float W5 = SW5 * CW5; + float W6 = SW6 * CW6; + float W7 = SW7 * CW7; + float W8 = SW8 * CW8; + float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; + + // Weighted color + float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + + C7 * W7 + C8 * W8) / + max(TotalWeight, 0.0001); + + // Blend it + float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); + float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); + float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); + float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); + float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); + float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); + float HDREdge = max(A0Max, A0Max2); + float EdgeMask = saturate(1.0f - HDREdge); + + return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); } ' @@ -30842,63 +36082,35 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEdgeDetectionShader { +function Get-OBSRippleShader { -[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] +[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] param( -# Set the sensitivity of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('sensitivity')] +# Set the distance_factor of OBSRippleShader +[Alias('distance_factor')] +[ComponentModel.DefaultBindingProperty('distance_factor')] [Single] -$Sensitivity, -# Set the invert_edge of OBSEdgeDetectionShader -[Alias('invert_edge')] -[ComponentModel.DefaultBindingProperty('invert_edge')] -[Management.Automation.SwitchParameter] -$InvertEdge, -# Set the edge_color of OBSEdgeDetectionShader -[Alias('edge_color')] -[ComponentModel.DefaultBindingProperty('edge_color')] -[String] -$EdgeColor, -# Set the edge_multiply of OBSEdgeDetectionShader -[Alias('edge_multiply')] -[ComponentModel.DefaultBindingProperty('edge_multiply')] -[Management.Automation.SwitchParameter] -$EdgeMultiply, -# Set the non_edge_color of OBSEdgeDetectionShader -[Alias('non_edge_color')] -[ComponentModel.DefaultBindingProperty('non_edge_color')] -[String] -$NonEdgeColor, -# Set the non_edge_multiply of OBSEdgeDetectionShader -[Alias('non_edge_multiply')] -[ComponentModel.DefaultBindingProperty('non_edge_multiply')] -[Management.Automation.SwitchParameter] -$NonEdgeMultiply, -# Set the alpha_channel of OBSEdgeDetectionShader -[Alias('alpha_channel')] -[ComponentModel.DefaultBindingProperty('alpha_channel')] -[Management.Automation.SwitchParameter] -$AlphaChannel, -# Set the alpha_level of OBSEdgeDetectionShader -[Alias('alpha_level')] -[ComponentModel.DefaultBindingProperty('alpha_level')] +$DistanceFactor, +# Set the time_factor of OBSRippleShader +[Alias('time_factor')] +[ComponentModel.DefaultBindingProperty('time_factor')] [Single] -$AlphaLevel, -# Set the alpha_invert of OBSEdgeDetectionShader -[Alias('alpha_invert')] -[ComponentModel.DefaultBindingProperty('alpha_invert')] -[Management.Automation.SwitchParameter] -$AlphaInvert, -# Set the rand_f of OBSEdgeDetectionShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$TimeFactor, +# Set the power_factor of OBSRippleShader +[Alias('power_factor')] +[ComponentModel.DefaultBindingProperty('power_factor')] [Single] -$RandF, -# Set the notes of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$PowerFactor, +# Set the center_pos_x of OBSRippleShader +[Alias('center_pos_x')] +[ComponentModel.DefaultBindingProperty('center_pos_x')] +[Single] +$CenterPosX, +# Set the center_pos_y of OBSRippleShader +[Alias('center_pos_y')] +[ComponentModel.DefaultBindingProperty('center_pos_y')] +[Single] +$CenterPosY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30929,102 +36141,54 @@ $UseShaderTime process { -$shaderName = 'edge_detection' -$ShaderNoun = 'OBSEdgeDetectionShader' +$shaderName = 'ripple' +$ShaderNoun = 'OBSRippleShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Edge Detection for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform float sensitivity< - string label = "Sensitivity"; +uniform float distance_factor< + string label = "distance factor"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; + float maximum = 100.0; float step = 0.001; -> = 0.05; -uniform bool invert_edge; -uniform float4 edge_color = {1.0,1.0,1.0,1.0}; -uniform bool edge_multiply; -uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; -uniform bool non_edge_multiply; -uniform bool alpha_channel; -uniform float alpha_level< - string label = "Alpha level"; +> = 12.0; +uniform float time_factor< + string label = "time factor"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool alpha_invert; -uniform float rand_f; - -uniform string notes< - string widget_type = "info"; -> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform float power_factor< + string label = "power factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 3.0; +uniform float center_pos_x< + string label = "center pos x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float center_pos_y< + string label = "center pos y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - - float s = 3; - float hstep = uv_pixel_interval.x; - float vstep = uv_pixel_interval.y; - - float offsetx = (hstep * s) / 2.0; - float offsety = (vstep * s) / 2.0; - - float4 lum = float4(0.30, 0.59, 0.11, 1 ); - float samples[9]; - - int index = 0; - for(int i = 0; i < s; i++){ - for(int j = 0; j < s; j++){ - samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); - index++; - } - } - - float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; - float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; - float4 col; - - float o = ((vert * vert) + (hori * hori)); - bool isEdge = o > sensitivity; - if(invert_edge){ - isEdge = !isEdge; - } - if(isEdge) { - col = edge_color; - if(edge_multiply){ - col *= c; - } - } else { - col = non_edge_color; - if(non_edge_multiply){ - col *= c; - } - } - - if (alpha_invert) { - lum = 1.0 - lum; - } - - if(alpha_channel){ - if (edge_multiply && isEdge) { - return clamp(lerp(c, col, alpha_level), 0.0, 1.0); - } - else { - // use max instead of multiply - return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); - } - } else { - // col.a = col.a * alpha_level; - return col; - } + float2 cPos = (v_in.uv * 2 ) -1; + float2 center_pos = float2(center_pos_x, center_pos_y); + float cLength = distance(cPos, center_pos); + float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; + return image.Sample(textureSampler, uv); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -31122,114 +36286,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbersShader { +function Get-OBSRotatingSourceShader { -[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] +[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] param( -# Set the ViewProj of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSEmbersShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSEmbersShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSEmbersShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSEmbersShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSEmbersShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSEmbersShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSEmbersShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSEmbersShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSEmbersShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the notes of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Speed of OBSEmbersShader -[Alias('Animation_Speed')] -[ComponentModel.DefaultBindingProperty('Animation_Speed')] -[Single] -$AnimationSpeed, -# Set the Movement_Direction_Horizontal of OBSEmbersShader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +# Set the spin_speed of OBSRotatingSourceShader +[Alias('spin_speed')] +[ComponentModel.DefaultBindingProperty('spin_speed')] [Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSEmbersShader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +$SpinSpeed, +# Set the rotation of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('rotation')] [Single] -$MovementDirectionVertical, -# Set the Movement_Speed_Percent of OBSEmbersShader -[Alias('Movement_Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] -[Int32] -$MovementSpeedPercent, -# Set the Layers_Count of OBSEmbersShader -[Alias('Layers_Count')] -[ComponentModel.DefaultBindingProperty('Layers_Count')] -[Int32] -$LayersCount, -# Set the lumaMin of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMin')] +$Rotation, +# Set the zoomin of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('zoomin')] [Single] -$LumaMin, -# Set the lumaMinSmooth of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +$Zoomin, +# Set the keep_aspectratio of OBSRotatingSourceShader +[Alias('keep_aspectratio')] +[ComponentModel.DefaultBindingProperty('keep_aspectratio')] +[Management.Automation.SwitchParameter] +$KeepAspectratio, +# Set the x_center of OBSRotatingSourceShader +[Alias('x_center')] +[ComponentModel.DefaultBindingProperty('x_center')] [Single] -$LumaMinSmooth, -# Set the Alpha_Percentage of OBSEmbersShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +$XCenter, +# Set the y_center of OBSRotatingSourceShader +[Alias('y_center')] +[ComponentModel.DefaultBindingProperty('y_center')] [Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSEmbersShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, +$YCenter, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -31260,372 +36348,86 @@ $UseShaderTime process { -$shaderName = 'embers' -$ShaderNoun = 'OBSEmbersShader' +$shaderName = 'rotating-source' +$ShaderNoun = 'OBSRotatingSourceShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -uniform string notes< - string widget_type = "info"; -> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; - -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif - -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -uniform float Animation_Speed < - string label = "Animation Speed"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; - float scale = 1.; -> = 1.5; - -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 5.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 10.0; - -uniform int Movement_Speed_Percent< - string label = "Movement Speed Percent"; +//spin speed higher the slower +uniform float spin_speed< + string label = "Spin Speed"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 5; - -uniform int Layers_Count < - string label = "Layers"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float rotation< + string label = "Rotation"; string widget_type = "slider"; - int minimum = 1.0; - int maximum = 100.0; - int step = 1; -> = 15; -/* ps start -*/ - - -uniform float lumaMin< - string label = "Luma Min"; + float minimum = -360.0; + float maximum = 360.0; + float step = 0.1; +> = 0.0; +uniform float zoomin< + string label = "Zoom"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; + float minimum = 0.01; + float maximum = 10.0; float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; +> = 1.0; +uniform bool keep_aspectratio = true; +uniform float x_center< + string label = "Center x"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; + float step = 0.001; +> = 0.5; +uniform float y_center< + string label = "Center y"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform bool Apply_To_Alpha_Layer = true; - -#define PI 3.1415927 -#define TWO_PI 6.283185 - -#define PARTICLE_SIZE 0.009 - -#define PARTICLE_SCALE float2(0.5, 1.6) -#define PARTICLE_SCALE_VAR float2(0.25, 0.2) - -#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) -#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) - -#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 -#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 -#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 - -#define SIZE_MOD 1.05 -#define ALPHA_MOD 0.9 -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) -#define Movement_Speed Movement_Speed_Percent * 0.01 -#define UV float2(fragCoord.xy / uv_size) - -float hash1_2(float2 x) -{ - return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); -} - -float2 hash2_2(float2 x) -{ - mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); - float2 y = mul(x, m); - return fract(sin(y) * 492.194); -} - -//Simple interpolated noise -float2 noise2_2(float2 uv) -{ - //float2 f = fract(uv); - float2 f = smoothstep(0.0, 1.0, fract(uv)); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - float2 v00 = hash2_2(uv00); - float2 v01 = hash2_2(uv01); - float2 v10 = hash2_2(uv10); - float2 v11 = hash2_2(uv11); - - float2 v0 = mix(v00, v01, f.y); - float2 v1 = mix(v10, v11, f.y); - float2 v = mix(v0, v1, f.x); - - return v; -} + float maximum = 1.0; + float step = 0.001; +> = 0.5; -//Simple interpolated noise -float noise1_2(float2 uv) -{ - float2 f = fract(uv); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - - float v00 = hash1_2(uv00); - float v01 = hash1_2(uv01); - float v10 = hash1_2(uv10); - float v11 = hash1_2(uv11); - - float v0 = mix(v00, v01, f.y); - float v1 = mix(v10, v11, f.y); - float v = mix(v0, v1, f.x); - - return v; -} -float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) +//main fragment code +//from lioran to nutella with love +float4 mainImage(VertData v_in) : TARGET { - float noise = 0.0; - float alpha = 1.0; - float size = 1.0; - float2 offset; - for (int i = 0; i < layers; i++) - { - offset += hash2_2(float2(alpha, size)) * 10.0; - - //Adding noise with movement - noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; - alpha *= alphaMod; - size *= sizeMod; + float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; + float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; + //get position on of the texture and focus on the middle + float i_rotation; + if (spin_speed == 0){ + //turn angle number into pi number + i_rotation = rotation/57.295779513; + }else{ + //use elapsed time for spinning if spin speed is not 0 + i_rotation = elapsed_time * spin_speed; } - - noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); - return noise; -} - -//Rotates point around 0,0 -float2 rotate(float2 vpoint, float deg) -{ - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - return mul(vpoint, m); -} - -//Cell center from point on the grid -float2 voronoiPointFromRoot(float2 root, float deg) -{ - float2 vpoint = hash2_2(root) - 0.5; - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - vpoint = mul(vpoint, m) * 0.66; - vpoint += root + 0.5; - return vpoint; -} - -//Voronoi cell point rotation degrees -float degFromRootUV(in float2 uv) -{ - return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; -} - -float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) -{ - return vpoint + (hash2_2(uv) - 0.5) * range; -} - - -float3 fireParticles(in float2 uv, in float2 originalUV) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float2 rootUV = floor(uv); - float deg = degFromRootUV(rootUV); - float2 pointUV = voronoiPointFromRoot(rootUV, deg); - float dist = 2.0; - float distBloom = 0.0; - - //UV manipulation for the faster particle movement - float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; - tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; - - //Sparks sdf - dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); - - //Bloom sdf - distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); - - //Add sparks - particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; - - //Add bloom - particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; - - //Upper disappear curve randomization - float border = (hash1_2(rootUV) - 0.5) * 2.0; - float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); - - //Lower appear curve randomization - border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; - float appear = smoothstep(border, border + 0.4, originalUV.y); - - return particles * disappear * appear; -} - - -//Layering particles to imitate 3D view -float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float size = 1.0; - float alpha = 1.0; - float2 offset = float2(0.0, 0.0); - float2 noiseOffset; - float2 bokehUV; - - for (int i = 0; i < layers; i++) - { - //Particle noise movement - noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; - - //UV with applied movement - bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; + float2 i_point; + i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); + i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); - //Adding particles if there is more smoke, remove smaller particles - particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); + //get the angle from center , returns pi number + float i_dir = atan(i_point.y/i_point.x); + if(i_point.x < 0.0){ + i_dir += 3.14159265359; + } - //Moving uv origin to avoid generating the same particles - offset += hash2_2(float2(alpha, alpha)) * 10.0; + //get the distance from the centers + float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); + //multiple distance by the zoomin value + i_distance *= zoomin; - alpha *= alphaMod; - size *= sizeMod; - } - - return particles; -} - - -void mainImage(out float4 fragColor, in float2 fragCoord) -{ - float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; - float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); - - uv *= 1.8; - float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); - - float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); - smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); - float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; - - //Cutting holes in smoke - smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), - 2.0) * 1.5; - - float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); - - float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); - col.rgb *= vignette; - col.rgb = smoothstep(-0.08, 1.0, col.rgb); - - if (Apply_To_Alpha_Layer) - { - float4 original_color = image.Sample(textureSampler, UV); - - float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min, 0.0, 1.0); - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } - - fragColor = col; -} - -/*ps end*/ - -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} - -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} - -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } + //shift the texture position based on angle and distance from the middle + i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; + i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; + + //draw normally from new point + return image.Sample(textureSampler, i_point); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -31723,35 +36525,99 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossColorShader { +function Get-OBSRotatoeShader { -[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] +[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] param( -# Set the Angle_Steps of OBSEmbossColorShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSEmbossColorShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSEmbossColorShader -[ComponentModel.DefaultBindingProperty('ampFactor')] +# Set the ViewProj of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSRotatoeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$AmpFactor, -# Set the Up_Down_Percent of OBSEmbossColorShader -[Alias('Up_Down_Percent')] -[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] +$ElapsedTime, +# Set the uv_offset of OBSRotatoeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRotatoeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRotatoeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSRotatoeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSRotatoeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed_percent of OBSRotatoeShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] [Int32] -$UpDownPercent, -# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +$SpeedPercent, +# Set the Axis_X of OBSRotatoeShader +[Alias('Axis_X')] +[ComponentModel.DefaultBindingProperty('Axis_X')] +[Single] +$AxisX, +# Set the Axis_Y of OBSRotatoeShader +[Alias('Axis_Y')] +[ComponentModel.DefaultBindingProperty('Axis_Y')] +[Single] +$AxisY, +# Set the Axis_Z of OBSRotatoeShader +[Alias('Axis_Z')] +[ComponentModel.DefaultBindingProperty('Axis_Z')] +[Single] +$AxisZ, +# Set the Angle_Degrees of OBSRotatoeShader +[Alias('Angle_Degrees')] +[ComponentModel.DefaultBindingProperty('Angle_Degrees')] +[Single] +$AngleDegrees, +# Set the Rotate_Transform of OBSRotatoeShader +[Alias('Rotate_Transform')] +[ComponentModel.DefaultBindingProperty('Rotate_Transform')] [Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSEmbossColorShader +$RotateTransform, +# Set the Rotate_Pixels of OBSRotatoeShader +[Alias('Rotate_Pixels')] +[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] +[Management.Automation.SwitchParameter] +$RotatePixels, +# Set the Rotate_Colors of OBSRotatoeShader +[Alias('Rotate_Colors')] +[ComponentModel.DefaultBindingProperty('Rotate_Colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the center_width_percentage of OBSRotatoeShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRotatoeShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# Set the notes of OBSRotatoeShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -31785,89 +36651,157 @@ $UseShaderTime process { -$shaderName = 'emboss_color' -$ShaderNoun = 'OBSEmbossColorShader' +$shaderName = 'rotatoe' +$ShaderNoun = 'OBSRotatoeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; +// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; + +uniform int speed_percent< + string label = "speed percentage"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; + int minimum = -100; + int maximum = 100; int step = 1; -> = 9; // -uniform int Radius_Steps< - string label = "Radius Steps"; +> = 50; // +uniform float Axis_X< + string label = "Axis X"; string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 4; // -uniform float ampFactor< - string label = "amp Factor"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.1; +> = 0.0; +uniform float Axis_Y< + string label = "Axis Y"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; + float minimum = -2.0; + float maximum = 2.0; float step = 0.01; -> = 12.0; -uniform int Up_Down_Percent< - string label = "Up Down Percent"; +> = 0.0; +uniform float Axis_Z< + string label = "Axis Z"; string widget_type = "slider"; - int minimum = -100; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; +uniform float Angle_Degrees< + string label = "Angle Degrees"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 0.01; +> = 45.0; +uniform bool Rotate_Transform = true; +uniform bool Rotate_Pixels = false; +uniform bool Rotate_Colors = false; +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; int maximum = 100; int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; + uniform string notes< string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; +> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; -float4 mainImage(VertData v_in) : TARGET +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float3x3 rotAxis(float3 axis, float a) { + float s=sin(a); + float c=cos(a); + float oc=1.0-c; + + float3 as=axis*s; + + float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); + float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); + return p*oc+q; +} + +VertData mainTransform(VertData v_in) { - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (1 * uv_pixel_interval.y); - float maxRadius = (6 * uv_pixel_interval.y); + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - float embossAngle = 0.25 * PI; + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); + // use matrix to transform rotation + if (Rotate_Transform) + vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; - if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) - { - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; + vert_out.uv = v_in.uv * uv_scale + uv_offset; - for (float angle = 0; angle < (2 * PI); angle += angleDelta) { - float2 currentCoord; + return vert_out; +} - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; - } - } - accumulatedColor *= ampFactor; + // use matrix to transform pixels + if (Rotate_Pixels) + { + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); + } + if (Rotate_Colors) + rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; - c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); } - //return c0 + accumulatedColor; // down; - //return c0 - accumulatedColor; // up - return c0; } ' @@ -31967,20 +36901,45 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossShader { +function Get-OBSRoundedRect2Shader { -[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] +[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] param( -# Set the Use_Color of OBSEmbossShader -[Alias('Use_Color')] -[ComponentModel.DefaultBindingProperty('Use_Color')] -[Management.Automation.SwitchParameter] -$UseColor, -# Set the Apply_To_Alpha_Layer of OBSEmbossShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +# Set the corner_radius of OBSRoundedRect2Shader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedRect2Shader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRect2Shader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRect2Shader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRect2Shader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRect2Shader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# Set the faster_scan of OBSRoundedRect2Shader +[Alias('faster_scan')] +[ComponentModel.DefaultBindingProperty('faster_scan')] [Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, +$FasterScan, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32011,44 +36970,172 @@ $UseShaderTime process { -$shaderName = 'emboss' -$ShaderNoun = 'OBSEmbossShader' +$shaderName = 'rounded_rect2' +$ShaderNoun = 'OBSRoundedRect2Shader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform bool Use_Color; -uniform bool Apply_To_Alpha_Layer = true; +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float border_alpha_end< + string label = "Border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float alpha_cut_off< + string label = "Aplha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; +uniform bool faster_scan = true; float4 mainImage(VertData v_in) : TARGET { + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + float check_dist = float(corner_radius); + if(border_thickness > check_dist) + check_dist = border_thickness; + if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(-check_dist); + } + if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(-check_dist); + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(!faster_scan || closedEdgeX != 0){ + [loop] for(int x = 1;float(x) 0.0 || Apply_To_Alpha_Layer == false) - { - float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); - float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); - float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); - float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); - float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); - float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); - - c0 = (-c1 - c2 - c4 + c6 + c8 + c9); - float c = (c0.r + c0.g + c0.b) / 3 + 0.5; - c0 = float4(c,c,c,c); - - if (Use_Color) - { - float4 rgba = image.Sample(textureSampler, v_in.uv); - return (0.5 * rgba) + c0; - } - } - return c0; + float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(check_dist,check_dist)); + if(d > check_dist && border_thickness > corner_radius){ + if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ + float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(floor(cd) > corner_radius) + return float4(0.0,0.0,0.0,0.0); + if(cd > corner_radius){ + d = border_thickness + cd - corner_radius; + } else if(d > border_thickness){ + d = border_thickness; + } + }else if(d > border_thickness){ + d = border_thickness; + } + } + if(floor(d) <= check_dist){ + if(border_thickness > 0){ + if(ceil(check_dist-d) <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + if(border_alpha_start < border_alpha_end){ + fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; + fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); + } + if(d > check_dist) + fade_color.a *= 1.0 -(d - check_dist); + return fade_color; + }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ + float4 fade_color = border_color; + float f; + if(border_thickness > (check_dist-d)) + f = border_thickness - (check_dist-d); + else + f = 1.0 -((check_dist-d) - border_thickness); + fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; + return fade_color; + } + } + if (d > check_dist) + pixel.a *= 1.0 - (d - check_dist); + return pixel; + + } + return float4(0.0,0.0,0.0,0.0); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32146,60 +37233,55 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSExeldroBentCameraShader { +function Get-OBSRoundedRectPerCornerShader { -[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] +[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] param( -# Set the left_side_width of OBSExeldroBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSExeldroBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSExeldroBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSExeldroBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSExeldroBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSExeldroBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSExeldroBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSExeldroBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] +# Set the corner_radius_tl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tl')] +[ComponentModel.DefaultBindingProperty('corner_radius_tl')] +[Int32] +$CornerRadiusTl, +# Set the corner_radius_tr of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tr')] +[ComponentModel.DefaultBindingProperty('corner_radius_tr')] +[Int32] +$CornerRadiusTr, +# Set the corner_radius_br of OBSRoundedRectPerCornerShader +[Alias('corner_radius_br')] +[ComponentModel.DefaultBindingProperty('corner_radius_br')] +[Int32] +$CornerRadiusBr, +# Set the corner_radius_bl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_bl')] +[ComponentModel.DefaultBindingProperty('corner_radius_bl')] +[Int32] +$CornerRadiusBl, +# Set the border_thickness of OBSRoundedRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] [Single] -$RightSideShadow, -# Set the right_flip_width of OBSExeldroBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] [Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSExeldroBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] [Single] -$RightFlipShadow, +$AlphaCutOff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32230,126 +37312,185 @@ $UseShaderTime process { -$shaderName = 'exeldro-bent-camera' -$ShaderNoun = 'OBSExeldroBentCameraShader' +$shaderName = 'rounded_rect_per_corner' +$ShaderNoun = 'OBSRoundedRectPerCornerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_radius_tl< + string label = "Corner radius top left"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_tr< + string label = "Corner radius top right"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_br< + string label = "Corner radius bottom right"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; - -uniform float right_side_width< - string label = "Right side width"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_bl< + string label = "Corner radius bottom left"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.6; + float step = 0.001; +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width / 2.0; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width / 2.0; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); } - - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; + int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; + int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; + int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; + int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; + + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_radius_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_radius_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_radius_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(d = 0; +uniform int corner_radius_left< + string label = "Corner radius left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_top< + string label = "Corner radius top"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_right< + string label = "Corner radius right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; > = 0.5; -uniform bool convert_linear = true; float4 mainImage(VertData v_in) : TARGET { - float4 a_val = image_a.Sample(textureSampler, v_in.uv); - float4 b_val = image_b.Sample(textureSampler, v_in.uv); - float4 rgba = lerp(a_val, b_val, transition_time); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; + float4 output_color = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(output_color.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return output_color; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x corner_radius){ + closedEdgeXabs = 0; + } + if(closedEdgeYabs > corner_radius){ + closedEdgeYabs = 0; + } + if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ + return output_color; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } + + float closest = closedEdgeXabs = 0.500; - -uniform float Gradient_Width< - string label = "Gradient Width"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 0.15; // Adjust the maximum value as needed - float step = 0.01; -> = 0.05; - -uniform float Gradient_Offset< - string label = "Gradient Offset"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; string widget_type = "slider"; - float minimum = 0; - float maximum = 0.100; // Adjust the maximum value as needed - float step = 0.005; -> = 0.00; - -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Bottom"; - int option_3_value = 3; - string option_3_label = "Top"; + int minimum = 0; + int maximum = 100; + int step = 1; > = 0; -uniform float4 Fill_Color; +uniform float4 border_color; float4 mainImage(VertData v_in) : TARGET { - float distanceToEdge = 0.0; - - // Calculate distance to the fill edge based on the selected direction - if (Fill_Direction == 0) - distanceToEdge = Fill - v_in.uv.x; - else if (Fill_Direction == 1) - distanceToEdge = v_in.uv.x - (1.0 - Fill); - else if (Fill_Direction == 2) - distanceToEdge = v_in.uv.y - (1.0 - Fill); - else if (Fill_Direction == 3) - distanceToEdge = Fill - v_in.uv.y; - - // Calculate the gradient factor based on the distance to the edge and the gradient width - float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); - float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; - - // Adjust distanceToEdge by the Gradient_Offset - distanceToEdge += gradientOffset; - - // Normalize the distance to be between 0 and 1 - distanceToEdge = saturate(distanceToEdge); - - // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); - // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); - float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); - - // Blend between the fill color and the original image color using the gradient factor - float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); - - return finalColor; + float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); + float4 output_color = image.Sample(textureSampler, v_in.uv); + + float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); + float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); + + bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; + bool is_within_radius = pixel_distance_from_center <= corner_radius; + + bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); + bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); + + return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); } ' @@ -32855,24 +38107,54 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorLinearShader { +function Get-OBSRoundedStrokeGradientShader { -[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] +[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] param( -# Set the Fill of OBSFillColorLinearShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Fill_Direction of OBSFillColorLinearShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] +# Set the corner_radius of OBSRoundedStrokeGradientShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] [Int32] -$FillDirection, -# Set the Fill_Color of OBSFillColorLinearShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeGradientShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the rotation_speed of OBSRoundedStrokeGradientShader +[Alias('rotation_speed')] +[ComponentModel.DefaultBindingProperty('rotation_speed')] +[Int32] +$RotationSpeed, +# Set the border_colorL of OBSRoundedStrokeGradientShader +[Alias('border_colorL')] +[ComponentModel.DefaultBindingProperty('border_colorL')] [String] -$FillColor, +$BorderColorL, +# Set the border_colorR of OBSRoundedStrokeGradientShader +[Alias('border_colorR')] +[ComponentModel.DefaultBindingProperty('border_colorR')] +[String] +$BorderColorR, +# Set the center_width of OBSRoundedStrokeGradientShader +[Alias('center_width')] +[ComponentModel.DefaultBindingProperty('center_width')] +[Int32] +$CenterWidth, +# Set the center_height of OBSRoundedStrokeGradientShader +[Alias('center_height')] +[ComponentModel.DefaultBindingProperty('center_height')] +[Int32] +$CenterHeight, +# Set the notes of OBSRoundedStrokeGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32903,57 +38185,197 @@ $UseShaderTime process { -$shaderName = 'fill_color_linear' -$ShaderNoun = 'OBSFillColorLinearShader' +$shaderName = 'rounded_stroke_gradient' +$ShaderNoun = 'OBSRoundedStrokeGradientShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Fill< - string label = "Fill"; +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +uniform int corner_radius< + string label = "Corner radius"; string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.005; ->; -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Top"; - int option_3_value = 3; - string option_3_label = "Bottom"; + int minimum = 0; + int maximum = 200; + int step = 1; > = 0; -uniform float4 Fill_Color; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int rotation_speed< + string label = "rotation speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_colorL; +uniform float4 border_colorR; +//uniform float color_spread = 2.0; +uniform int center_width< + string label = "center width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height< + string label = "center height"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; + +// float3 hsv2rgb(float3 c) +// { +// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); +// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); +// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +// } + +float mod(float x, float y) +{ + return x - y * floor(x/y); +} + +float4 gradient(float c) { + c = mod(c , 2.0); + if(c < 0.0f){ + c = c * -1.0; + } + if(c > 1.0){ + c = 1.0 - c; + if(c < 0.0f){ + c = c + 1.0; + } + } + return lerp(border_colorL, border_colorR, c); +} + +float4 getBorderColor(float2 toCenter){ + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; + return gradient((angle / 3.14159265f) + angleMod); +} float4 mainImage(VertData v_in) : TARGET { - bool is_inside_fill = true; - - // Check if the pixel is within the specified "fill width" on the left side - if(Fill_Direction == 0){ - is_inside_fill = v_in.uv.x > Fill; + float2 st = v_in.uv * uv_scale; + float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); + float2 toCenter = center_pixel_coordinates - st; + + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); } - if(Fill_Direction == 1) + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) { - is_inside_fill = v_in.uv.x < (1.0 - Fill); + closedEdgeX = corner_radius; } - if(Fill_Direction == 2) + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) { - is_inside_fill = v_in.uv.y > Fill; - } - if(Fill_Direction == 3) + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) { - is_inside_fill = v_in.uv.y < (1.0 - Fill); + closedEdgeY = corner_radius; } - - // Invert is_inside_fill - is_inside_fill = !is_inside_fill; - - // If inside the "fill," make the pixel selected colour; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return output_color; + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + } + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; + } + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; + } + } + + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; + } + } + return float4(0.0, 0.0, 0.0, 0.0); } ' } @@ -33052,39 +38474,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialDegreesShader { +function Get-OBSRoundedStrokeShader { -[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] +[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] param( -# Set the Fill_Direction of OBSFillColorRadialDegreesShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] +# Set the corner_radius of OBSRoundedStrokeShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] [Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialDegreesShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialDegreesShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] -[Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialDegreesShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialDegreesShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialDegreesShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the border_color of OBSRoundedStrokeShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] [String] -$FillColor, +$BorderColor, +# Set the notes of OBSRoundedStrokeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33115,97 +38532,140 @@ $UseShaderTime process { -$shaderName = 'fill_color_radial_degrees' -$ShaderNoun = 'OBSFillColorRadialDegreesShader' +$shaderName = 'rounded_stroke' +$ShaderNoun = 'OBSRoundedStrokeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; - -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 360; - float step = 1.00000; ->; - -uniform float Start_Angle< - string label = "Start Angle"; +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 +uniform int corner_radius< + string label = "Corner radius"; string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; - -uniform float Offset_X< - string label = "Offset X"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float Offset_Y< - string label = "Offset Y"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float4 Fill_Color; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float4 border_color; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; float4 mainImage(VertData v_in) : TARGET { - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); - - // Convert angle from radians to degrees - angle = degrees(angle); - - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return float4(output_color); + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + } + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return border_color; + } + else + { + return float4(output_color); + } + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return border_color; + } + else + { + return float4(output_color); + } } - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; - - // Check if the angle is within the specified "fill width" - bool is_inside_fill = angle < Fill; - - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return border_color; + } + else + { + return output_color; + } + } + return float4(0.0, 0.0, 0.0, 0.0); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -33303,39 +38763,46 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialPercentageShader { +function Get-OBSScanLineShader { -[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] +[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] param( -# Set the Fill_Direction of OBSFillColorRadialPercentageShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialPercentageShader -[ComponentModel.DefaultBindingProperty('Fill')] +# Set the lengthwise of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('lengthwise')] +[Management.Automation.SwitchParameter] +$Lengthwise, +# Set the animate of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the speed of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('speed')] [Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialPercentageShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] +$Speed, +# Set the angle of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('angle')] [Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialPercentageShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] +$Angle, +# Set the shift of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('shift')] +[Management.Automation.SwitchParameter] +$Shift, +# Set the boost of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('boost')] +[Management.Automation.SwitchParameter] +$Boost, +# Set the floor of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('floor')] [Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialPercentageShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] +$Floor, +# Set the period of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('period')] [Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialPercentageShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] +$Period, +# Set the notes of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$FillColor, +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33366,98 +38833,104 @@ $UseShaderTime process { -$shaderName = 'fill_color_radial_percentage' -$ShaderNoun = 'OBSFillColorRadialPercentageShader' +$shaderName = 'scan_line' +$ShaderNoun = 'OBSScanLineShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; - -uniform float Fill< - string label = "Fill"; +// Scan Line Effect for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +//Count the number of scanlines we want via height or width, adjusts the sin wave period +uniform bool lengthwise; +//Do we want the scanlines to move? +uniform bool animate; +//How fast do we want those scanlines to move? +uniform float speed< + string label = "Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.00005; -> = 0.0; - -uniform float Start_Angle< - string label = "Start Angle"; + float maximum = 10000.0; + float step = 1; +> = 1000; +//What angle should the scanlines come in at (based in degrees) +uniform float angle< + string label = "angle"; string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; - -uniform float Offset_X< - string label = "Offset X"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 45; +//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit +//By default values for color range from 0 to 1 +//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen +uniform bool shift = true; +uniform bool boost = true; +//Increases the minimum value of the sin wave +uniform float floor< + string label = "Floor"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; > = 0.0; - -uniform float Offset_Y< - string label = "Offset Y"; +//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input +uniform float period< + string label = "Period"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float4 Fill_Color; - + float minimum = 1.0; + float maximum = 1000.0; + float step = 1.0; +> = 10.0; +uniform string notes< + string widget_type = "info"; +> = "floor affects the minimum opacity of the scan line"; float4 mainImage(VertData v_in) : TARGET { - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); - - // Convert angle from radians to degrees - angle = degrees(angle); - - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; - } - - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; - - // Calculate the percentage of the angle - float anglePercentage = angle / 360.0; - - // Check if the angle percentage is within the specified "fill percentage" - bool is_inside_fill = anglePercentage < Fill; - - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); + //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 + // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 + float nfloor = clamp(floor, 0.0, 100.0) * 0.01; + float nperiod = max(period, 1.0); + float gap = 1 - nfloor; + float pi = 3.1415926535897932384626; + float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); + float nspeed = 0.0; + if(animate){ + nspeed = speed * 0.0001; + } + + float4 color = image.Sample(textureSampler, v_in.uv); + + float t = elapsed_time * nspeed; + + if(!lengthwise){ + float base_height = 1.0 / uv_pixel_interval.y; + float h_interval = pi * base_height; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } + else{ + float base_width = 1.0 / uv_pixel_interval.x; + float w_interval = pi * base_width; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } } ' @@ -33557,71 +39030,54 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFilterTemplateShader { +function Get-OBSSeascapeShader { -[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] +[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] param( -# Set the ViewProj of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSFilterTemplateShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFilterTemplateShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFilterTemplateShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFilterTemplateShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFilterTemplateShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFilterTemplateShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +# Set the AA of OBSSeascapeShader +[ComponentModel.DefaultBindingProperty('AA')] +[Management.Automation.SwitchParameter] +$AA, +# Set the SEA_HEIGHT of OBSSeascapeShader +[Alias('SEA_HEIGHT')] +[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] [Single] -$RandF, -# Set the rand_instance_f of OBSFilterTemplateShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] +$SEAHEIGHT, +# Set the SEA_CHOPPY of OBSSeascapeShader +[Alias('SEA_CHOPPY')] +[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] [Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFilterTemplateShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] +$SEACHOPPY, +# Set the SEA_SPEED of OBSSeascapeShader +[Alias('SEA_SPEED')] +[ComponentModel.DefaultBindingProperty('SEA_SPEED')] [Single] -$RandActivationF, -# Set the loops of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFilterTemplateShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] +$SEASPEED, +# Set the SEA_FREQ of OBSSeascapeShader +[Alias('SEA_FREQ')] +[ComponentModel.DefaultBindingProperty('SEA_FREQ')] [Single] -$LocalTime, -# Set the notes of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('notes')] +$SEAFREQ, +# Set the SEA_BASE of OBSSeascapeShader +[Alias('SEA_BASE')] +[ComponentModel.DefaultBindingProperty('SEA_BASE')] [String] -$Notes, +$SEABASE, +# Set the SEA_WATER_COLOR of OBSSeascapeShader +[Alias('SEA_WATER_COLOR')] +[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] +[String] +$SEAWATERCOLOR, +# Set the CAMERA_SPEED of OBSSeascapeShader +[Alias('CAMERA_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] +[Single] +$CAMERASPEED, +# Set the CAMERA_TURN_SPEED of OBSSeascapeShader +[Alias('CAMERA_TURN_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] +[Single] +$CAMERATURNSPEED, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33652,75 +39108,282 @@ $UseShaderTime process { -$shaderName = 'filter_template' -$ShaderNoun = 'OBSFilterTemplateShader' +$shaderName = 'seascape' +$ShaderNoun = 'OBSSeascapeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//My shader modified by Me for use with obs-shaderfilter month/year v.02 +/* + * "Seascape" by Alexander Alekseev aka TDM - 2014 + * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + * Contact: tdmaav@gmail.com + * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro + */ -//Section to converting GLSL to HLSL - can delete if unneeded -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 +#define NUM_STEPS 8 +#define PI 3.141592 +#define EPSILON 0.001 +uniform bool AA< + string label = "Smooth (more resources)"; +> = false; + +#ifndef OPENGL #define mat2 float2x2 #define mat3 float3x3 -#define mat4 float4x4 #define fract frac #define mix lerp -#define iTime float -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) - -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +#endif -uniform float4x4 ViewProj; -uniform texture2d image; +// sea +#define ITER_GEOMETRY 3 +#define ITER_FRAGMENT 5 +uniform float SEA_HEIGHT< + string label = "Sea Height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.5; + float step = 0.001; +> = 0.6; +uniform float SEA_CHOPPY< + string label = "Sea Choppy"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 4.0; +uniform float SEA_SPEED< + string label = "Sea Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.8; +uniform float SEA_FREQ< + string label = "Sea Frequency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.16; +uniform float4 SEA_BASE< + string label = "Sea Base"; +> = {0.0,0.09,0.18,1.0}; +uniform float4 SEA_WATER_COLOR< + string label = "Sea Water"; +> = {0.48,0.54,0.36,1.0}; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ -uniform string notes< - string widget_type = "info"; -> = "add notes here"; +uniform float CAMERA_SPEED< + string label = "Camera Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; + +uniform float CAMERA_TURN_SPEED< + string label = "Camera Turn Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); +float SEA_TIME(){ + return 1.0 + elapsed_time * SEA_SPEED; } -/* -**Shaders use the built in Draw technique** -**this section can be deleted** +// math +mat3 fromEuler(float3 ang) { + float2 a1 = float2(sin(ang.x),cos(ang.x)); + float2 a2 = float2(sin(ang.y),cos(ang.y)); + float2 a3 = float2(sin(ang.z),cos(ang.z)); + return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), + float3(-a2.y*a1.x,a1.y*a2.y,a2.x), + float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); +} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } +float hash(float2 p) { + float h = dot(p,float2(127.1,311.7)); + return fract(sin(h)*43758.5453123); +} + +float noise(float2 p) { + float2 i = floor( p ); + float2 f = fract( p ); + float2 u = f*f*(3.0-2.0*f); + return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), + hash( i + float2(1.0,0.0) ), u.x), + mix( hash( i + float2(0.0,1.0) ), + hash( i + float2(1.0,1.0) ), u.x), u.y); +} + +// lighting +float diffuse(float3 n,float3 l,float p) { + return pow(dot(n,l) * 0.4 + 0.6,p); +} +float specular(float3 n,float3 l,float3 e,float s) { + float nrm = (s + 8.0) / (PI * 8.0); + return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; +} + +// sky +float3 getSkyColor(float3 e) { + e.y = (max(e.y,0.0)*0.8+0.2)*0.8; + return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +} + +// sea +float sea_octave(float2 uv, float choppy) { + uv += noise(uv); + float2 wv = 1.0-abs(sin(uv)); + float2 swv = abs(cos(uv)); + wv = mix(wv,swv,wv); + return pow(1.0-pow(wv.x * wv.y,0.65),choppy); +} + +float map(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; + uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_GEOMETRY; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float map_detailed(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_FRAGMENT; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { + float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); + fresnel = min(pow(fresnel,3.0), 0.5); + + float3 reflected = getSkyColor(reflect(eye,n)); + float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; + + float3 color = mix(refracted,reflected,fresnel); + + float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); + color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; + + float s = specular(n,l,eye,60.0); + color += float3(s,s,s); + + return color; +} + +// tracing +float3 getNormal(float3 p, float eps) { + float3 n; + n.y = map_detailed(p); + n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; + n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; + n.y = eps; + return normalize(n); +} + +float heightMapTracing(float3 ori, float3 dir, out float3 p) { + float tm = 0.0; + float tx = 1000.0; + float hx = map(ori + dir * tx); + if(hx > 0.0) { + p = ori + dir * tx; + return tx; + } + float hm = map(ori + dir * tm); + float tmid = 0.0; + for(int i = 0; i < NUM_STEPS; i++) { + tmid = mix(tm,tx, hm/(hm-hx)); + p = ori + dir * tmid; + float hmid = map(p); + if(hmid < 0.0) { + tx = tmid; + hx = hmid; + } else { + tm = tmid; + hm = hmid; + } + } + return tmid; +} + +float3 getPixel(in float2 coord, float time) { + float2 uv = coord / uv_size.xy; + uv = uv * 2.0 - 1.0; + uv.x *= uv_size.x / uv_size.y; + + // ray + float3 ang = float3(sin(time*3.0*CAMERA_TURN_SPEED)*0.1,sin(time*CAMERA_TURN_SPEED)*0.2+0.3,time*CAMERA_TURN_SPEED); + float3 ori = float3(0.0,3.5,time*5.0*CAMERA_SPEED); + float3 dir = normalize(float3(uv.xy,-2.0)); + dir.z += length(uv) * 0.14; + dir = mul(normalize(dir), fromEuler(ang)); + + // tracing + float3 p; + heightMapTracing(ori,dir,p); + float3 dist = p - ori; + float3 n = getNormal(p, dot(dist,dist) * (0.1 / uv_size.x)); + float3 light = normalize(float3(0.0,1.0,0.8)); + + // color + return mix( + getSkyColor(dir), + getSeaColor(p,n,light,dir,dist), + pow(smoothstep(0.0,-0.02,dir.y),0.2)); } -*/ +// main +float4 mainImage(VertData v_in) : TARGET +{ + float time = elapsed_time * 0.3; + float2 fragCoord = float2(v_in.uv.x * uv_size.x, (1.0 - v_in.uv.y) * uv_size.y); + + float3 color = float3(0.0,0.0,0.0);; + if (AA){ + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + float2 uv = fragCoord+float2(i,j)/3.0; + color += getPixel(uv, time); + } + } + color /= 9.0; + }else{ + color = getPixel(fragCoord, time); + } + + // post + return float4(pow(color,float3(0.65,0.65,0.65)), 1.0); +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -33818,143 +39481,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFire3Shader { +function Get-OBSSeasickShader { -[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] +[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] param( -# Set the ViewProj of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('image')] +# Set the notes of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$Image, -# Set the elapsed_time of OBSFire3Shader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFire3Shader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFire3Shader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFire3Shader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFire3Shader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFire3Shader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSFire3Shader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFire3Shader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFire3Shader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the Movement_Direction_Horizontal of OBSFire3Shader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +$Notes, +# Set the amplitude of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('amplitude')] [Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSFire3Shader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +$Amplitude, +# Set the speed of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('speed')] [Single] -$MovementDirectionVertical, -# Set the Alpha_Percentage of OBSFire3Shader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] $Speed, -# Set the Invert of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFire3Shader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFire3Shader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSFire3Shader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSFire3Shader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Full_Width of OBSFire3Shader -[Alias('Full_Width')] -[ComponentModel.DefaultBindingProperty('Full_Width')] -[Management.Automation.SwitchParameter] -$FullWidth, -# Set the Flame_Size of OBSFire3Shader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Single] -$FlameSize, -# Set the Spark_Grid_Height of OBSFire3Shader -[Alias('Spark_Grid_Height')] -[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] -[Single] -$SparkGridHeight, -# Set the Flame_Modifier of OBSFire3Shader -[Alias('Flame_Modifier')] -[ComponentModel.DefaultBindingProperty('Flame_Modifier')] +# Set the frequency of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('frequency')] [Single] -$FlameModifier, -# Set the Flame_Tongue_Size of OBSFire3Shader -[Alias('Flame_Tongue_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] +$Frequency, +# Set the opacity of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('opacity')] [Single] -$FlameTongueSize, +$Opacity, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33985,444 +39535,52 @@ $UseShaderTime process { -$shaderName = 'fire-3' -$ShaderNoun = 'OBSFire3Shader' +$shaderName = 'seasick' +$ShaderNoun = 'OBSSeasickShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//My effect modified by Me for use with obs-shaderfilter month/year v.02 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp - - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; - -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; +// Seasick - an effect for OBS Studio +// +uniform string notes< + string widget_type = "info"; +> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; +uniform float amplitude< + string label = "amplitude"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.03; +uniform float speed< + string label = "speed"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; + float minimum = 0.0; + float maximum = 10.0; float step = 0.01; -> = 0.0; - -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) - -uniform int Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 80; -uniform bool Invert = false; -uniform float lumaMin< - string label = "Luma Min"; +> = 1.0; +uniform float frequency< + string label = "frequency"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; + float maximum = 100.0; float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; +> = 6.0; +uniform float opacity< + string label = "opacity"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.04; -uniform bool Apply_To_Image = true; -uniform bool Replace_Image_Color = true; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color = false; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float2 uv = v_in.uv; - if(Invert) - uv = 1.0 - v_in.uv; - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} + float step = 0.001; +> = 0.5; -int2 iMouse() +float4 mainImage(VertData v_in) : TARGET { - return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); + float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); + float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); + return image.Sample(textureSampler, coord) * opacity; } - -float mod(float x, float y) -{ - return x - y * floor(x / y); -} - -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x / y); -} - -/*ps start*/ -#define PI 3.1415926535897932384626433832795 -uniform bool Full_Width = false; - -uniform float Flame_Size< - string label = "Flame Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; - -uniform float Spark_Grid_Height< - string label = "Spark Grid Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; - -uniform float Flame_Modifier< - string label = "Flame Modifier"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float Flame_Tongue_Size< - string label = "Flame Tongue Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 8.5; - -// -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// - -vec3 mod2893(vec3 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 mod289(vec4 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} - -vec4 permute(vec4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} - -vec4 taylorInvSqrt(vec4 r) -{ - return 1.79284291400159 - 0.85373472095314 * r; -} - -float snoise(vec3 v) -{ - const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); - -// First corner - vec3 i = floor(v + dot(v, C.yyy)); - vec3 x0 = v - i + dot(i, C.xxx); - -// Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min(g.xyz, l.zxy); - vec3 i2 = max(g.xyz, l.zxy); - - // x0 = x0 - 0.0 + 0.0 * C.xxx; - // x1 = x0 - i1 + 1.0 * C.xxx; - // x2 = x0 - i2 + 2.0 * C.xxx; - // x3 = x0 - 1.0 + 3.0 * C.xxx; - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y - -// Permutations - i = mod2893(i); - vec4 p = permute(permute(permute( - i.z + vec4(0.0, i1.z, i2.z, 1.0)) - + i.y + vec4(0.0, i1.y, i2.y, 1.0)) - + i.x + vec4(0.0, i1.x, i2.x, 1.0)); - -// Gradients: 7x7 points over a square, mapped onto an octahedron. -// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; - - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) - - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) - - vec4 x = x_ * ns.x + ns.yyyy; - vec4 y = y_ * ns.x + ns.yyyy; - vec4 h = 1.0 - abs(x) - abs(y); - - vec4 b0 = vec4(x.xy, y.xy); - vec4 b1 = vec4(x.zw, y.zw); - - //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; - //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; - vec4 s0 = floor(b0) * 2.0 + 1.0; - vec4 s1 = floor(b1) * 2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); - - vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; - vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; - - vec3 p0 = vec3(a0.xy, h.x); - vec3 p1 = vec3(a0.zw, h.y); - vec3 p2 = vec3(a1.xy, h.z); - vec3 p3 = vec3(a1.zw, h.w); - -//Normalise gradients - //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); - vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); - p0 *= norm.x; - p1 *= norm.y; - p2 *= norm.z; - p3 *= norm.w; - -// Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); - m = m * m; - return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); -} - -////////////////////////////////////////////////////////////// - -// PRNG -// From https://www.shadertoy.com/view/4djSRW -float prng(in vec2 seed) -{ - seed = fract(seed * vec2(5.3983, 5.4427)); - seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); - return fract(seed.x * seed.y * 95.4337); -} - -////////////////////////////////////////////////////////////// - -float noiseStack(vec3 pos, int octaves, float falloff) -{ - float noise = snoise(vec3(pos)); - float off = 1.0; - if (octaves > 1) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 2) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 3) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - return (1.0 + noise) / 2.0; -} - -vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) -{ - float displaceA = noiseStack(pos, octaves, falloff); - float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); - return vec2(displaceA, displaceB); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 UV = (1.0 - v_in.uv) * uv_scale; - if (Invert) - UV = v_in.uv * uv_scale; - float alpha = saturate(Alpha_Percentage * .01); - float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); - - vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); - if (Full_Width) - { - resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; - - } - resolution.x = mul(resolution.x, 1 / 1); - float time = iTime * (Speed * 0.01); - //vec2 drag = iMouse().xy; - vec2 offset = iMouse().xy; - // - float xpart = UV.x / resolution.x; - float ypart = UV.y / resolution.y; - // - - float ypartClip = UV.y / ( flame_size * 75.0); - float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); - float ypartClipped = min(ypartClip, 1.0); - float ypartClippedn = (1 - ypartClipped); - // - float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); - // - float timeSpeed = 0.5 * (Speed * 0.01); - float realTime = -1.0 * timeSpeed * time; - // - vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; - vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); - vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); - vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; - // - vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); - vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); - // - vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; - float noise = noiseStack(noiseCoord, 3, 0.4); - // - float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); - // - float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); - float fff = f * f * f; - vec3 fire = 1.5 * vec3(f, fff, fff * fff); - // - // smoke - float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; - float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); - vec3 smoke = vec3(smokePart, smokePart, smokePart); - // - // sparks - float sparkGridSize = Spark_Grid_Height; - vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); - sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); - sparkCoord += 100.0 * flow.xy; - if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) - sparkCoord.x += 0.5 * sparkGridSize; - vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); - float sparkRandom = prng( sparkGridIndex); - float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); - vec3 sparks = vec3(0.0, 0.0, 0.0); - if (sparkLife > 0.0) - { - float sparkSize = xfuel * xfuel * sparkRandom * 0.08; - float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; - vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); - vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; - vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); - float sparkLength = length(sparkModulus); - float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); - sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); - } - // - float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire, 0.0, alpha); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); - } - else - { - rgba = color; - } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - - return rgba; -} - -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} - -' +' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { @@ -34519,65 +39677,64 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireShader { +function Get-OBSSelectiveColorShader { -[Alias('Set-OBSFireShader','Add-OBSFireShader')] +[Alias('Set-OBSSelectiveColorShader','Add-OBSSelectiveColorShader')] param( -# Set the Alpha_Percentage of OBSFireShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFireShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] -$Speed, -# Set the Flame_Size of OBSFireShader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Int32] -$FlameSize, -# Set the Fire_Type of OBSFireShader -[Alias('Fire_Type')] -[ComponentModel.DefaultBindingProperty('Fire_Type')] -[Int32] -$FireType, -# Set the Invert of OBSFireShader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMin')] +# Set the cutoff_Red of OBSSelectiveColorShader +[Alias('cutoff_Red')] +[ComponentModel.DefaultBindingProperty('cutoff_Red')] [Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +$CutoffRed, +# Set the cutoff_Green of OBSSelectiveColorShader +[Alias('cutoff_Green')] +[ComponentModel.DefaultBindingProperty('cutoff_Green')] [Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFireShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +$CutoffGreen, +# Set the cutoff_Blue of OBSSelectiveColorShader +[Alias('cutoff_Blue')] +[ComponentModel.DefaultBindingProperty('cutoff_Blue')] +[Single] +$CutoffBlue, +# Set the cutoff_Yellow of OBSSelectiveColorShader +[Alias('cutoff_Yellow')] +[ComponentModel.DefaultBindingProperty('cutoff_Yellow')] +[Single] +$CutoffYellow, +# Set the acceptance_Amplifier of OBSSelectiveColorShader +[Alias('acceptance_Amplifier')] +[ComponentModel.DefaultBindingProperty('acceptance_Amplifier')] +[Single] +$AcceptanceAmplifier, +# Set the show_Red of OBSSelectiveColorShader +[Alias('show_Red')] +[ComponentModel.DefaultBindingProperty('show_Red')] [Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFireShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +$ShowRed, +# Set the show_Green of OBSSelectiveColorShader +[Alias('show_Green')] +[ComponentModel.DefaultBindingProperty('show_Green')] [Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSFireShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +$ShowGreen, +# Set the show_Blue of OBSSelectiveColorShader +[Alias('show_Blue')] +[ComponentModel.DefaultBindingProperty('show_Blue')] [Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSFireShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Notes of OBSFireShader -[ComponentModel.DefaultBindingProperty('Notes')] +$ShowBlue, +# Set the show_Yellow of OBSSelectiveColorShader +[Alias('show_Yellow')] +[ComponentModel.DefaultBindingProperty('show_Yellow')] +[Management.Automation.SwitchParameter] +$ShowYellow, +# Set the notes of OBSSelectiveColorShader +[ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, +# Set the background_type of OBSSelectiveColorShader +[Alias('background_type')] +[ComponentModel.DefaultBindingProperty('background_type')] +[Int32] +$BackgroundType, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34608,255 +39765,127 @@ $UseShaderTime process { -$shaderName = 'fire' -$ShaderNoun = 'OBSFireShader' +$shaderName = 'selective_color' +$ShaderNoun = 'OBSSelectiveColorShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 -// https://github.com/Oncorporation/obs-shaderfilter plugin -// https://www.shadertoy.com/view/MtcGD7 original version -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -//v.5 -// flicker -// flame type -// apply to image -// replace image color -// speed -// flame size -// alpha -// invert direction/position - - -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp - -uniform int Alpha_Percentage< - string label = "Aplha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; +// Selective Color shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//updated 4/13/2020: take into account the opacity/alpha of input image -thanks Skeletonbow for suggestion +//Converted to OpenGL by Q-mii February 25, 2020 +uniform float cutoff_Red< + string label = "cutoff Red"; string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 100; -uniform int Flame_Size< - string label = "Flame Size"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.40; +uniform float cutoff_Green< + string label = "cutoff Green"; string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 70; -uniform int Fire_Type< - string label = "Fire Type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Smaller and more whisps"; - int option_1_value = 1; - string option_1_label = "Larger and more volume"; -> = 1; - -uniform bool Invert < - string name = "Invert"; -> = false; -uniform float lumaMin< - string label = "Luma min"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.025; +uniform float cutoff_Blue< + string label = "cutoff Blue"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma min smooth"; +> = 0.25; +uniform float cutoff_Yellow< + string label = "cutoff Yellow"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; -> = 0.04; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string Notes< - string widget_type = "info"; -> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; - -vec3 rgb2hsv(vec3 c) -{ - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) -{ - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -float rand(vec2 n) -{ - return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); - //return sin(rand_f, n); -} - -float noise(vec2 n) -{ - const vec2 d = vec2(0.0, 1.0); - vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); - return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); -} +> = 0.25; +uniform float acceptance_Amplifier< + string label = "acceptance Amplifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 5.0; -float fbm(vec2 n) -{ - float total = 0.0, amplitude = 1.0; - for (int i = 0; i < 5; i++) - { - total += noise(n) * amplitude; - n += n * 1.7; - amplitude *= 0.47; - } - return total; -} +uniform bool show_Red = true; +uniform bool show_Green = true; +uniform bool show_Blue = true; +uniform bool show_Yellow = true; +uniform string notes< + string widget_type = "info"; +> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; +uniform int background_type< + string label = "background type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Grey"; + int option_1_value = 1; + string option_1_label = "Luma"; + int option_2_value = 2; + string option_2_label = "White"; + int option_3_value = 3; + string option_3_label = "Black"; + int option_4_value = 4; + string option_4_label = "Transparent"; + int option_5_value = 5; + string option_5_label = "Background Color"; +> = 0; float4 mainImage(VertData v_in) : TARGET { - float2 iResolution = uv_scale; - float flame_size = clamp(Flame_Size * .01,-5,5); - - // inverting direction is logically inverted to allow the bottom up to be normal - float fire_base = (v_in.uv.y / iResolution.y); - float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); - float direction = -1.0 * clamp(Speed*.01,-5,5); - if (!Invert) - { - direction *= -1.0; - fire_base = 1 - fire_base; - fire_pix = 1 - fire_pix; - } - float iTime = direction * elapsed_time; - - const vec3 c1 = vec3(0.5, 0.0, 0.1); - const vec3 c2 = vec3(0.9, 0.1, 0.0); - const vec3 c3 = vec3(0.2, 0.1, 0.7); - const vec3 c4 = vec3(1.0, 0.9, 0.1); - const vec3 c5 = vec3(0.1, 0.1, 0.1); - const vec3 c6 = vec3(0.9, 0.9, 0.9); + const float PI = 3.1415926535897932384626433832795;//acos(-1); + const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); + float4 color = image.Sample(textureSampler, v_in.uv); - vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); - float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; - float alpha = saturate(Alpha_Percentage * .01); - - //change the constant term for all kinds of cool distance versions, - //make plus/minus to switch between - //ground fire and fire rain! - float dist = 3.5 - sin(iTime * 0.4) / 1.89; - - vec2 p = fire_pix * dist / iResolution.xx; - p.x -= iTime / 1.1; - float3 black = float3(0,0,0); - vec3 fire; + float luminance = dot(coefLuma, color.rgb); + float4 gray = float4(luminance, luminance, luminance, 1.0); - if (Fire_Type == 1) - { - //fire version 1 larger and more volume - float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); - float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); - float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; - float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; - q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; + if (background_type == 0) + { + luminance = (color.r + color.g + color.b) * 0.3333; + gray = float4(luminance,luminance,luminance, 1.0); + } + //if (background_type == 1) + //{ + // gray = float4(luminance,luminance,luminance, 1.0); + //} + if (background_type == 2) + gray = float4(1.0,1.0,1.0,1.0); + if (background_type == 3) + gray = float4(0.0,0.0,0.0,1.0); + if (background_type == 4) + gray.a = 0.01; + if (background_type == 5) + gray = color; - vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), - fbm(p - q - iTime* speed.y)) ; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); + float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); + float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); + float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); + + float rgLuminance = (color.r*1.4 + color.g*0.6)/2; + float rgDiff = abs(color.r-color.g)*1.4; - fire += .05; - fire.r *= .8; - vec3 hsv = rgb2hsv(fire); - hsv.y *= hsv.z * 1.1; - hsv.z *= hsv.y * 1.13; - hsv.y = (2.2 - hsv.z * .9) * 1.20; - fire = hsv2rgb(hsv); - } - else - { - // fire version 0 - smaller and more whisps - p += (rand_f *.01); - float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); - float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); - float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; - float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; - q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; + float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; - vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), - fbm(p + q - iTime * speed.y)) * shift; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); - - fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); - fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); - fire = fire/(1.0+max(black,fire)); - } - float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire,0.0,alpha); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); - } - else - { - rgba = color; - } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - return rgba; -} + float4 accept; + accept.r = float(show_Red) * (redness - cutoff_Red); + accept.g = float(show_Green) * (greenness - cutoff_Green); + accept.b = float(show_Blue) * (blueness - cutoff_Blue); + accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); + float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); + float modAcceptance = min (acceptance * acceptance_Amplifier, 1); + float4 result = color; + if (result.a > 0) { + result = modAcceptance * color + (1.0 - modAcceptance) * gray; + //result.a *= gray.a; + } + return result; +} ' } @@ -34955,14 +39984,85 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworks2Shader { +function Get-OBSShakeShader { -[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] +[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] param( -# Set the Speed of OBSFireworks2Shader -[ComponentModel.DefaultBindingProperty('Speed')] +# Set the ViewProj of OBSShakeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSShakeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSShakeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSShakeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSShakeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSShakeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSShakeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSShakeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the local_time of OBSShakeShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the random_scale of OBSShakeShader +[Alias('random_scale')] +[ComponentModel.DefaultBindingProperty('random_scale')] +[Single] +$RandomScale, +# Set the worble of OBSShakeShader +[ComponentModel.DefaultBindingProperty('worble')] +[Management.Automation.SwitchParameter] +$Worble, +# Set the speed of OBSShakeShader +[ComponentModel.DefaultBindingProperty('speed')] [Single] $Speed, +# Set the min_growth_pixels of OBSShakeShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] +[Single] +$MinGrowthPixels, +# Set the max_growth_pixels of OBSShakeShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, +# Set the randomize_movement of OBSShakeShader +[Alias('randomize_movement')] +[ComponentModel.DefaultBindingProperty('randomize_movement')] +[Management.Automation.SwitchParameter] +$RandomizeMovement, +# Set the notes of OBSShakeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34993,129 +40093,148 @@ $UseShaderTime process { -$shaderName = 'fireworks2' -$ShaderNoun = 'OBSFireworks2Shader' +$shaderName = 'shake' +$ShaderNoun = 'OBSShakeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/4dBGRw +// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +// Added some randomization based upon random_scale input +// updated for latest version of obs-shaderfilter -uniform float Speed< +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform float local_time; + + +uniform float random_scale< + string label = "random scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.25; +uniform bool worble = false; +uniform float speed< string label = "Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 200.0; - float step = 1.0; -> = 100.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = -2.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform bool randomize_movement = false; -#ifndef OPENGL -#define mat2 float2x2 -#define mix lerp -float mod(float x, float y) -{ - return x - y * floor(x / y); -} -#endif -//Creates a diagonal red-and-white striped pattern. -float3 barberpole(float2 pos, float2 rocketpos){ - float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); - float3 col=float3(1.0,1.0,1.0); +uniform string notes< + string widget_type = "info"; +> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; - d = mod(d*20.,2.0); - if(d>1.0){ - col=float3(1.0,0.0,0.0); - } - return col; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +//noise values in range if 0.0 to 1.0 + +float noise3D(float x, float y, float z) { + float ptr = 0.0f; + return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); } -float4 rocket(float2 pos, float2 rocketpos){ - float4 col = float4(0.0,0.0,0.0,0.0); - float f = 0.; - float absx= abs(rocketpos.x - pos.x); - float absy = abs(rocketpos.y-pos.y); - //wooden stick - if(absx<0.01&&absy<0.22){ - col=float4(1.0,0.5,0.5,1.0); - } - - //Barberpole - - if(absx<0.05&&absy<0.15){ - col=float4(barberpole(pos, rocketpos),1.0); +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + + float3 pos = v_in.pos.xyz; + float t; + float s; + float noise; + if (randomize_movement) + { + t = (rand_f * 2) - 1.0f; + s = (1 - rand_f * 2) - 1.0f;; + noise = clamp( rand_f * random_scale,-0.99, 0.99); } - //Rocket Point - float pointw=(rocketpos.y-pos.y-0.25)*-0.7; - if((rocketpos.y-pos.y)>0.1){ - f=smoothstep(pointw-0.001,pointw+0.001,absx); - - col=mix(float4(1.0,0.0,0.0,1.0),col, f); + else + { + t = (1 + sin(elapsed_time * speed)) / 2; + s = (1 + cos(elapsed_time * speed)) / 2; + noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); } - //Shadow - - f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); - col.rgb *= 0.7+f; - - return col; -} - + float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); + float3 min_pos; + float3 max_pos; + if (worble) + { + min_pos = pos + direction_from_center * min_growth_pixels * 0.5; + max_pos = pos + direction_from_center * max_growth_pixels * 0.5; + } + else + { + min_pos = pos + direction_from_center * 0.5; + max_pos = min_pos; + } -float rand(float val, float seed){ - return cos(val*sin(val*seed)*seed); -} + float3 current_pos = min_pos * (1 - t) + max_pos * t; + //current_pos.x = v_in.pos.x + (t * min_pos.x); + current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); + //current_pos.y = v_in.pos.y + (s * min_pos.y); + //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; -float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } + float2 offset = uv_offset; + offset.x = uv_offset.x * (1 - t + noise); + offset.y = uv_offset.y * (1 - s + noise); + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + + //float2 scale = uv_scale; + //scale += dot(pos - current_pos, 1); + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} -float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ - float4 col= float4(0.0,0.0,0.0,0.0); - float2 pp = float2(1.0,0.0); - mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); - for(float i=1.0;i<=128.0;i++){ - float d=rand(i, seed); - float fade=(i/128.0)*time; - float2 particpos = cpos + time*pp*d; - pp = mul(rr,pp); - col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); - } - col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); - col.a = col.r+col.g+col.b; - return col; +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); } -float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ - - float timeoffset = 2.0; - float4 col=float4(0.0,0.0,0.0,0.0); - if(time<=0.){ - return col; - } - time *= Speed /100.0; - if(mod(time, 6.0)>timeoffset){ - col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); - }else{ - - col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); } - return col; } -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; - uv.y = -uv.y; - uv.x *= uv_size.x/uv_size.y; - float4 col = image.Sample(textureSampler, v_in.uv); - //col.rgb += 0.1*uv.y; - float4 c; - c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); - col = mix(col, c, c.a); - - return col; -} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -35213,25 +40332,75 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworksShader { +function Get-OBSShineShader { -[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] +[Alias('Set-OBSShineShader','Add-OBSShineShader')] param( -# Set the show_flash of OBSFireworksShader -[Alias('show_flash')] -[ComponentModel.DefaultBindingProperty('show_flash')] +# Set the l_tex of OBSShineShader +[Alias('l_tex')] +[ComponentModel.DefaultBindingProperty('l_tex')] +[String] +$LTex, +# Set the shine_color of OBSShineShader +[Alias('shine_color')] +[ComponentModel.DefaultBindingProperty('shine_color')] +[String] +$ShineColor, +# Set the speed_percent of OBSShineShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the gradient_percent of OBSShineShader +[Alias('gradient_percent')] +[ComponentModel.DefaultBindingProperty('gradient_percent')] +[Int32] +$GradientPercent, +# Set the delay_percent of OBSShineShader +[Alias('delay_percent')] +[ComponentModel.DefaultBindingProperty('delay_percent')] +[Int32] +$DelayPercent, +# Set the Apply_To_Alpha_Layer of OBSShineShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] [Management.Automation.SwitchParameter] -$ShowFlash, -# Set the show_stars of OBSFireworksShader -[Alias('show_stars')] -[ComponentModel.DefaultBindingProperty('show_stars')] +$ApplyToAlphaLayer, +# Set the ease of OBSShineShader +[ComponentModel.DefaultBindingProperty('ease')] [Management.Automation.SwitchParameter] -$ShowStars, -# Set the use_transparancy of OBSFireworksShader -[Alias('use_transparancy')] -[ComponentModel.DefaultBindingProperty('use_transparancy')] +$Ease, +# Set the hide of OBSShineShader +[ComponentModel.DefaultBindingProperty('hide')] [Management.Automation.SwitchParameter] -$UseTransparancy, +$Hide, +# Set the reverse of OBSShineShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# Set the One_Direction of OBSShineShader +[Alias('One_Direction')] +[ComponentModel.DefaultBindingProperty('One_Direction')] +[Management.Automation.SwitchParameter] +$OneDirection, +# Set the glitch of OBSShineShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSShineShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the start_adjust of OBSShineShader +[Alias('start_adjust')] +[ComponentModel.DefaultBindingProperty('start_adjust')] +[Single] +$StartAdjust, +# Set the stop_adjust of OBSShineShader +[Alias('stop_adjust')] +[ComponentModel.DefaultBindingProperty('stop_adjust')] +[Single] +$StopAdjust, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35262,175 +40431,183 @@ $UseShaderTime process { -$shaderName = 'fireworks' -$ShaderNoun = 'OBSFireworksShader' +$shaderName = 'shine' +$ShaderNoun = 'OBSShineShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif +// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +// use color to control shine amount, use transition wipes or make your own alpha texture +// slerp not currently used, for circular effects +//Converted to OpenGL by Exeldro February 14, 2022 +uniform texture2d l_tex; +uniform float4 shine_color ; +uniform int speed_percent< + string label = "speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 25; +uniform int gradient_percent< + string label = "gradient percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; +uniform int delay_percent< + string label = "delay percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool ease = false; +uniform bool hide = false; +uniform bool reverse = false; +uniform bool One_Direction = true; +uniform bool glitch = false; +uniform string notes< + string widget_type = "info"; +> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; -uniform bool show_flash = true; -uniform bool show_stars = true; -uniform bool use_transparancy = true; +uniform float start_adjust; +uniform float stop_adjust; -float distLine(float2 p, float2 a, float2 b) { - float2 pa = p - a; - float2 ba = b - a; - float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * t); +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; } -float linef(float2 uv, float2 a, float2 b, float w) { - //return smoothstep(w, w - 0.01, distLine(uv, a, b)); - return w / distLine(uv, a, b); +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; } -float N21(float2 p) { - p = fract(p * float2(233.34, 851.73)); - p += dot(p, p + 23.45); - return fract(p.x * p.y); +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; } -float2 N22(float2 p) { - float n = N21(p); - return float2(n, N21(p + n)); +float4 slerp(float4 start, float4 end, float percent) +{ + // Dot product - the cosine of the angle between 2 vectors. + float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; + // Clamp it to be in the range of Acos() + // This may be unnecessary, but floating point + // precision can be a fickle mistress. + dotf = clamp(dotf, -1.0f, 1.0f); + // Acos(dot) returns the angle between start and end, + // And multiplying that by percent returns the angle between + // start and the final result. + float theta = acos(dotf)*percent; + float4 RelativeVec = normalize(end - start * dotf); + + // Orthonormal basis + // The final result. + return ((start*cos(theta)) + (RelativeVec*sin(theta))); } -float N11(float n) { - return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); -} +float4 mainImage(VertData v_in) : TARGET +{ + // convert input for vector math + float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); + float speed = speed_percent * 0.01; + float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); + float delay = clamp(delay_percent * 0.01, 0.0, 1.0); + -float particle(float2 uv, float2 p, float2 v, float r, float t) { - float g = -9.81; - float x = p.x + v.x * t; - float y = p.y + v.y * t + g / 2.0 * t * t; - float2 j = (float2(x, y) - uv) * 20.0; - float sparkle = 1.0 / dot(j, j); - return sparkle; -} + // circular easing variable + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = abs(sin(elapsed_time * speed)); -float2 p1(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); -} + // if time is greater than direction, we are going up! + direction = t - direction; -float2 p2(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); -} + // split into segments with frac or mod. + // delay is the gap between starting and ending of the sine wave, use speed to compensate + t = (frac(t) - delay) * (1 / (1 - delay)); + t = 1 + max(t,0.0); -float endTime(float h) { - return pow(h, 1.0 / 5.0) * 1.1; -} + float s = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration -float explosion(float2 uv, float2 p, float s, float n, float f, float t) { + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); - float m = 0.0; - float dt = 0.5; - float seed2 = 0.32; - for(float i = 0.0; i < n; i++) { - seed2 += i; - float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); - float2 v = float2(cos(seed2), sin(seed2)) + rand; - m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); - } - return m; -} + //if Unidirectional disable on return + if (One_Direction && (direction < 0.0)) + { + s = 0; + } + else + { + s = Styler(t, 0, c, d, ease); + } -float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { - float2 p1v = p1(p, h, t); - float e = endTime(h); - return explosion(uv, p1v, s, n, f, t - e * 0.9); -} + // combine luma texture and user defined shine color + float luma = l_tex.Sample(textureSampler, v_in.uv).x; -float shaft(float2 uv, float2 p, float w, float h, float t) { - float2 p1v = p1(p, h, t) + float2(0.0, 0.3); - float2 p2v = p2(p, h, t); - float e = 1.0 / 0.95 * endTime(h); - float2 j = (p1v - uv) * 15.0; - float sparkle = 1.0 / dot(j, j); - return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; -} + // - adjust for min and max + if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) + { -float3 base(float2 uv) { - return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); -} + if (reverse) + { + luma = 1.0 - luma; + } + + // user color with luma + float4 output_color = float4(shine_color.rgb, luma); -float back(float2 uv, float2 p, float t) { - float dt = 0.3; - float j = length(p - uv); - float m = exp(-0.005 * j * j); - return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); -} + float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); -float stars(float2 uv) { - float r = N21(uv); - return smoothstep(0.001, 0.0, r); -} + // use luma texture to add alpha and shine -float mod(float x, float y) -{ - return x - y * floor(x / y); -} + // if behind glow, consider trailing gradient shine then show underlying image + if (luma <= time - softness) + { + float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); + if (Apply_To_Alpha_Layer) + alpha_behind *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_behind); + } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv - float2(0.5,0.5); - uv.y = uv.y * -1; - float t = elapsed_time / 10.0; - float scale = 10.0; - uv *= scale; - // - float4 col = image.Sample(textureSampler, v_in.uv); - if(show_stars){ - float c = stars(uv); - if(use_transparancy){ - col += float4(c,c,c,c)*(1.0-col.a); - }else{ - col += float4(c,c,c,c);//*(1.0-orig_col.a); - } - - } - - float a = -0.035 * sin(t * 15.0); - float co = cos(a); - float si = sin(a); - mat2 trans1 = mat2(float2(co, si), float2(-si, co)); - float2 trans2 = float2(-15.0 * a, 0.0); -#ifndef OPENGL - uv = mul(uv, trans1); -#else - uv *= trans1; -#endif - uv += trans2; - - for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { - float ti = mod(t * 9.0 - i * 5.0, 4.0); - float scale = mix(2.0, 0.3, ti / 4.0); - float2 uvs = uv * scale; - float rand = N11(i); - float h = 10.0 + rand * 4.0; - float w = 0.02; - float n = 80.0; - float s = 0.9; - float f = 1.5; - float2 p = float2(mix(-8.0, 8.0, rand), -10.0); - float fw = fireworks(uvs, p, h, n, s, f, ti); - float3 bc = base(uv); - col += float4(bc*fw, fw); - col += shaft(uvs, p, w, h, ti); - if(show_flash){ - if(use_transparancy){ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; - }else{ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8); - } - } - } - - return col; + // if in front of glow, consider if the underlying image is hidden + if (luma >= time) + { + // if hide, make the transition better + if (hide) + { + return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); + } + else + { + return rgba; + } + } + + // else show the glow area, with luminance + float alpha_ = (time - luma) / softness; + if (Apply_To_Alpha_Layer) + alpha_ *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_); + } + else + { + return rgba; + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -35528,24 +40705,49 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeShader { +function Get-OBSSimpleGradientShader { -[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] +[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] param( -# Set the center_x_percent of OBSFisheyeShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Single] -$CenterYPercent, -# Set the power of OBSFisheyeShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, +# Set the speed_percentage of OBSSimpleGradientShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSSimpleGradientShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Lens_Flair of OBSSimpleGradientShader +[Alias('Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Lens_Flair')] +[Management.Automation.SwitchParameter] +$LensFlair, +# Set the Animate_Lens_Flair of OBSSimpleGradientShader +[Alias('Animate_Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] +[Management.Automation.SwitchParameter] +$AnimateLensFlair, +# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSSimpleGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSSimpleGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSSimpleGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35576,54 +40778,129 @@ $UseShaderTime process { -$shaderName = 'fisheye' -$ShaderNoun = 'OBSFisheyeShader' +$shaderName = 'simple_gradient' +$ShaderNoun = 'OBSSimpleGradientShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; +// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter + +//lots of room to play here +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform int speed_percentage< + string label = "speed percentage"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float power< - string label = "Power"; + int minimum = -500; + int maximum = 500; + int step = 1; +> = 240; // +uniform int alpha_percentage< + string label = "aplha percentage"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform bool Lens_Flair = false; +uniform bool Animate_Lens_Flair = false; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; float4 mainImage(VertData v_in) : TARGET { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); - } else if(power <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); - } - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 + float4 background_color = image.Sample(textureSampler, v_in.uv); + int no_colors = 4; + float3 colors[4]; + colors[0] = float3(1.0,0.0,0.0); + colors[1] = float3(0.0,1.0,0.0); + colors[2] = float3(0.0,0.0,1.0); + colors[3] = float3(1.0,1.0,1.0); + float alpha = float(alpha_percentage) * 0.01; + float speed = float(speed_percentage) * 0.01; + + float mx = max(uv_size.x , uv_size.y); + //float2 uv = v_in.uv / mx; + float3 rgb = background_color.rgb; + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(background_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + rgb = float3(v_in.uv.x, v_in.uv.y, 0.10 + 0.85 * sin(elapsed_time * speed)); + } + + //create lens flare like effect + if (Lens_Flair) + { + float2 lens_flair_coordinates = float2(0.95 ,0.95); + if (Animate_Lens_Flair) + lens_flair_coordinates *= float2(sin(elapsed_time * speed) ,cos(elapsed_time * speed)); + + float dist = distance(v_in.uv, ( lens_flair_coordinates * uv_scale + uv_offset)); + for (int i = 0; i < no_colors; ++i) { + rgb += lerp(rgb, colors[i], dist * 1.5) * 0.25; + } + } + + + //float3 col = colors[0]; +/* for (int i = 1; i < no_colors; ++i) { + float3 hole = float3( + sin(1.5 - distance(v_in.uv.x / mx, colors[i].x / mx) * 2.5 * speed), + sin(1.5 - distance(v_in.uv.y / mx, colors[i].y / mx) * 2.5 * speed), + colors[i].z); + rgb = lerp(rgb, hole, 0.1); +*/ +/* float3 hole = lerp(colors[i-1], colors[i], sin(elapsed_time * speed)); + col = lerp(col, hole, v_in.uv.x); +*/ + //} +// rgb = fflerp(rgb, col, 0.5); + + + + //try prepositioned colors with colors[] array timing + //creates an animated color spotlight +/* int color_index = int(sin(elapsed_time * speed) * no_colors); + float3 start_color = colors[color_index]; + float3 end_color; + if (color_index >= 0) + { + end_color = colors[color_index - 1]; + } + else + { + end_color = colors[no_colors - 1]; + } + + rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); +*/ + float4 rgba; + if (Apply_To_Alpha_Layer == false) + { + rgba = lerp(background_color,float4(rgb, 1.0),alpha); + } + else + { + rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); + } + if (Apply_To_Specific_Color) + { + float4 original_color = background_color; + background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; + rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); + } + return rgba; +} + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { $myNoun = $myVerb $myVerb = 'Get' @@ -35718,30 +40995,52 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeXyShader { +function Get-OBSSimplexNoiseShader { -[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] +[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] param( -# Set the center_x_percent of OBSFisheyeXyShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] +# Set the Snap_Percent of OBSSimplexNoiseShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] [Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeXyShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] +$SnapPercent, +# Set the Speed_Percent of OBSSimplexNoiseShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] [Single] -$CenterYPercent, -# Set the power_x of OBSFisheyeXyShader -[Alias('power_x')] -[ComponentModel.DefaultBindingProperty('power_x')] +$SpeedPercent, +# Set the Resolution of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Resolution')] [Single] -$PowerX, -# Set the power_y of OBSFisheyeXyShader -[Alias('power_y')] -[ComponentModel.DefaultBindingProperty('power_y')] +$Resolution, +# Set the Fractal of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Fractal')] +[Management.Automation.SwitchParameter] +$Fractal, +# Set the Use_Alpha_Layer of OBSSimplexNoiseShader +[Alias('Use_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$UseAlphaLayer, +# Set the Fore_Color of OBSSimplexNoiseShader +[Alias('Fore_Color')] +[ComponentModel.DefaultBindingProperty('Fore_Color')] +[String] +$ForeColor, +# Set the Back_Color of OBSSimplexNoiseShader +[Alias('Back_Color')] +[ComponentModel.DefaultBindingProperty('Back_Color')] +[String] +$BackColor, +# Set the Alpha_Percent of OBSSimplexNoiseShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] [Single] -$PowerY, +$AlphaPercent, +# Set the Notes of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35772,70 +41071,205 @@ $UseShaderTime process { -$shaderName = 'fisheye-xy' -$ShaderNoun = 'OBSFisheyeXyShader' +$shaderName = 'simplex_noise' +$ShaderNoun = 'OBSSimplexNoiseShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; +// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +//based upon: https://www.shadertoy.com/view/XsX3zB + +#ifndef OPENGL +#define fract frac +#endif + +uniform float Snap_Percent< + string label = "Snap Percent"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; +> = 7.5; +uniform float Speed_Percent< + string label = "Speed Percent"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; float step = 0.01; -> = 50.0; -uniform float power_x< - string label = "Power x"; +> = 2.5; +uniform float Resolution< + string label = "Resolution"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; + float minimum = 0.0; + float maximum = 100.0; float step = 0.01; -> = 1.75; -uniform float power_y< - string label = "Power y"; +> = 16.0; +uniform bool Fractal = false; +uniform bool Use_Alpha_Layer = false; +uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; +uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; +uniform float Alpha_Percent< + string label = "Alpha Percent"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; + float minimum = 0.0; + float maximum = 100.0; float step = 0.01; -> = 1.75; +> = 100.0; +uniform string Notes< + string widget_type = "info"; +> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; -float4 mainImage(VertData v_in) : TARGET +float dot(float3 a, float3 b){ + return a.r*b.r+a.g*b.g+a.b*b.b; +} + +float dot4(float4 a, float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} +float snap(float x, float snap) { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power_x >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; - } else if(power_x <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; - } - if (power_y >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; - } else if(power_y <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; - } - return image.Sample(textureSampler, uv); + return snap * round(x / max(0.01,snap)); +} + +float3 random3(float3 co) +{ + float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = fract(512.0 * j); + j *= .125; + result.x = fract(512.0 * j); + j *= .125; + result.y = fract(512.0 * j); + return result - 0.5; +} + +/* 3d simplex noise */ +float simplex3d(float3 p) { + /* 1. find current tetrahedron T and it''s four vertices */ + /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ + /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ + + /* skew constants for 3d simplex functions */ + float F3 = 0.3333333; + float G3 = 0.1666667; + + /* calculate s and x */ + float3 s = floor(p + dot(p, float3(F3,F3,F3))); + float3 x = p - s + dot(s, float3(G3,G3,G3)); + + /* calculate i1 and i2 */ + float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); + float3 i1 = e * (1.0 - e.zxy); + float3 i2 = 1.0 - e.zxy * (1.0 - e); + + /* x1, x2, x3 */ + float3 x1 = x - i1 + G3; + float3 x2 = x - i2 + 2.0 * G3; + float3 x3 = x - 1.0 + 3.0 * G3; + + /* 2. find four surflets and store them in d */ + float4 w, d; + + /* calculate surflet weights */ + w.x = dot(x, x); + w.y = dot(x1, x1); + w.z = dot(x2, x2); + w.w = dot(x3, x3); + + /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ + w = max(0.61 - w, 0.0); + + /* calculate surflet components */ + d.x = dot(random3(s), x); + d.y = dot(random3(s + i1), x1); + d.z = dot(random3(s + i2), x2); + d.w = dot(random3(s + 1.0), x3); + + /* multiply d by w^4 */ + w *= w; + w *= w; + d *= w; + + /* 3. return the sum of the four surflets */ + return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); +} + + +/* directional artifacts can be reduced by rotating each octave */ +float simplex3d_fractal(float3 m3) { + /* const matrices for 3d rotation */ +#ifdef OPENGL + float3x3 rot1 = float3x3( + float3(-0.37, 0.36, 0.85), + float3(-0.14, -0.93, 0.34), + float3(0.92, 0.01, 0.4 )); + float3x3 rot2 = float3x3( + float3(-0.55, -0.39, 0.74), + float3(0.33, -0.91, -0.24), + float3(0.77, 0.12, 0.63 )); + float3x3 rot3 = float3x3( + float3(-0.71, 0.52, -0.47), + float3(-0.08, -0.72, -0.68), + float3(-0.7, -0.45, 0.56 )); + + float3 m = float3(m3.x, m3.y, m3.z); +#else + float3x3 rot1 = { + -0.37, 0.36, 0.85, + -0.14, -0.93, 0.34, + 0.92, 0.01, 0.4 }; + float3x3 rot2 = { + -0.55, -0.39, 0.74, + 0.33, -0.91, -0.24, + 0.77, 0.12, 0.63 }; + float3x3 rot3 = { + -0.71, 0.52, -0.47, + -0.08, -0.72, -0.68, + -0.7, -0.45, 0.56 }; + + float3 m = {m3.x, m3.y, m3.z}; +#endif + + return 0.5333333* simplex3d(mul(m, rot1)) + + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) + + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) + + 0.0666667 * simplex3d(8.0 * m); } +float4 mainImage(VertData v_in) : TARGET +{ + float time = snap(elapsed_time, Snap_Percent * .01); + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 p = v_in.uv.xy + float2( 0, -0.5); + float3 p3 = float3(p, time * (Speed_Percent * 0.01)); + + float pixel_alpha = 1.0; + // apply to mainImage rgba + if (Use_Alpha_Layer) { + p3 *= rgba.rgb; + pixel_alpha = rgba.a; + } + + float value; + + if (Fractal) { + value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); + } + else { + value = simplex3d(p3 * Resolution); + } + + //soften color + value = 0.5 + (0.5 * value); + float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); + + //use intensity to apply foreground and background colors + float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); + r = lerp(Back_Color, r, saturate(intensity)); + r.a = pixel_alpha; + + return lerp(rgba, r, Alpha_Percent * 0.01); +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -35933,18 +41367,22 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFlipShader { +function Get-OBSSmartDenoiseShader { -[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] +[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] param( -# Set the Horizontal of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, +# Set the uSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uSigma')] +[Single] +$USigma, +# Set the uKSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uKSigma')] +[Single] +$UKSigma, +# Set the uThreshold of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uThreshold')] +[Single] +$UThreshold, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35975,30 +41413,85 @@ $UseShaderTime process { -$shaderName = 'Flip' -$ShaderNoun = 'OBSFlipShader' +$shaderName = 'smart_denoise' +$ShaderNoun = 'OBSSmartDenoiseShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// A Simple Flip Shader +// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) +// Converted to OBS version of HLSL by Euiko on February 10, 2025 -uniform bool Horizontal< - string label = "Flip horizontally"; -> = true; -uniform bool Vertical< - string label = "Flip vertically"; -> = true; +#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI +#define INV_PI 0.31830988618379067153776752674503 -float4 mainImage(VertData v_in) : TARGET +uniform float uSigma< + string label = "Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 3; // max based on the webgl sample, which is 3 + float step = 0.01; +> = 5.0; // default value based on shadertoy +uniform float uKSigma< + string label = "K-Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 24; // max based on the webgl sample, which is 24 + float step = 0.01; +> = 7.0; // the default value is based on the webgl sample +uniform float uThreshold< + string label = "Edge Threshold"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 2; // max based on the webgl sample, which is 2 + float step = 0.01; +> = 0.190; // the default value is based on the webgl sample + +// smartDeNoise - parameters +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// float2 uv - actual fragment coord +// float2 size - window size +// float sigma > 0 - sigma Standard Deviation +// float kSigma >= 0 - sigma coefficient +// kSigma * sigma --> radius of the circular kernel +// float threshold - edge sharpening threshold +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default +float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) { - float2 pos = v_in.uv; - if (Horizontal == true) { - pos.x = 1 - pos.x; - } - if (Vertical == true) { - pos.y = 1 - pos.y; + float radius = round(kSigma * sigma); + float radQ = radius * radius; + + float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) + float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) + + float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) + float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) + + float4 centrPx = image.Sample(textureSampler, uv); + + float zBuff = 0.0; + float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); + + float2 d; + for (d.x = -radius; d.x <= radius; d.x += 1.0) + { + float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend + d.y = -pt; + for (; d.y <= pt; d.y += 1.0) + { + float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; + float4 walkPx = image.Sample(textureSampler, uv + (d / size)); + float4 dC = walkPx - centrPx; + float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; + zBuff += deltaFactor; + aBuff += (walkPx * deltaFactor); + } } - - return image.Sample(textureSampler, pos); + return aBuff / float4(zBuff, zBuff, zBuff, zBuff); +} + +float4 mainImage(VertData v_in) : TARGET +{ + return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); } ' @@ -36098,46 +41591,47 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFrostedGlassShader { +function Get-OBSSpecularShineShader { -[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] +[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] param( -# Set the Alpha_Percent of OBSFrostedGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Amount of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Amount')] +# Set the Hint of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('Hint')] +[String] +$Hint, +# Set the roughness of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('roughness')] [Single] -$Amount, -# Set the Scale of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Scale')] +$Roughness, +# Set the lightStrength of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('lightStrength')] [Single] -$Scale, -# Set the Animate of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the Horizontal_Border of OBSFrostedGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSFrostedGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] +$LightStrength, +# Set the LightPositionX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionX')] [Single] -$BorderOffset, -# Set the Border_Color of OBSFrostedGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the notes of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$LightPositionX, +# Set the LightPositionY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionY')] +[Single] +$LightPositionY, +# Set the flattenNormal of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('flattenNormal')] +[Single] +$FlattenNormal, +# Set the stretchNormalX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalX')] +[Single] +$StretchNormalX, +# Set the stretchNormalY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalY')] +[Single] +$StretchNormalY, +# Set the Light_Color of OBSSpecularShineShader +[Alias('Light_Color')] +[ComponentModel.DefaultBindingProperty('Light_Color')] +[Single[]] +$LightColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36168,77 +41662,108 @@ $UseShaderTime process { -$shaderName = 'frosted_glass' -$ShaderNoun = 'OBSFrostedGlassShader' +$shaderName = 'specular-shine' +$ShaderNoun = 'OBSSpecularShineShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter +// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft -uniform float Alpha_Percent< - string label = "Alpha Percent"; +uniform string Hint< + string widget_type = "info"; +> = "Try using a black color source with the additive blend mode!"; + +uniform float roughness< + string label = "Roughness"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Amount< - string label = "Amount"; + float maximum = 1.0; + float step = 0.01; +> = 0.25; + +uniform float lightStrength< + string label = "lightStrength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; + +uniform float LightPositionX< + string label = "Light Position X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float LightPositionY< + string label = "Light Position Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float flattenNormal< + string label = "Flatten Normal"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.01; -> = 0.03; -uniform float Scale< - string label = "Scale"; +> = 0.1; + +uniform float stretchNormalX< + string label = "Stretch Normal X"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; + float maximum = 4.0; float step = 0.01; -> = 5.1; -uniform bool Animate; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; +> = 1; + +uniform float stretchNormalY< + string label = "Stretch Normal Y"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2.0; + float maximum = 4.0; float step = 0.01; -> = 1.1; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform string notes< - string widget_type = "info"; -> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; +> = 1; -float rand(float2 co) -{ - float scale = Scale; - if (Animate) - scale *= rand_f; - float2 v1 = float2(92.0,80.0); - float2 v2 = float2(41.0,62.0); - return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); +uniform float3 Light_Color = {1,1,1}; + +float Square(float a) { return a * a; } + +float CookTorrance(float3 lightDir, float3 normal, float roughness) { + float3 h = normalize(lightDir + float3(0,0,1)); + float nh2 = Square(saturate(dot(normal, h))); + float lh2 = Square(saturate(dot(lightDir, h))); + float r2 = Square(roughness); + float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); + float normalization = roughness * 4.0 + 2.0; + return r2 / (d2 * max(0.1, lh2) * normalization); } +#define PI 3.14159265 + float4 mainImage(VertData v_in) : TARGET { - float4 rgba = image.Sample(textureSampler, v_in.uv); - float3 tc = rgba.rgb * Border_Color.rgb; - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; - if (uv_compare < (Border_Offset - 0.005)) - { - float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); - tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - tc = image.Sample(textureSampler, v_in.uv).rgb; - } - return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); + float4 c0 = image.Sample(textureSampler, v_in.uv); + + float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); + + float2 normalUV = v_in.uv - 0.5; + normalUV.x /= stretchNormalX; + normalUV.y /= stretchNormalY; + normalUV += 0.5; + + float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); + normal.z *= -1; + + normal = lerp(normal, float3(0,0,-1), flattenNormal); + + float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; + + return float4(c0 + light,c0.a); } ' @@ -36338,24 +41863,41 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGammaCorrectionShader { +function Get-OBSSpotlightShader { -[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] +[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] param( -# Set the Red of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Red')] +# Set the Speed_Percent of OBSSpotlightShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] [Single] -$Red, -# Set the Green of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Green')] +$SpeedPercent, +# Set the Focus_Percent of OBSSpotlightShader +[Alias('Focus_Percent')] +[ComponentModel.DefaultBindingProperty('Focus_Percent')] [Single] -$Green, -# Set the Blue of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Blue')] +$FocusPercent, +# Set the Glitch of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Spotlight_Color of OBSSpotlightShader +[Alias('Spotlight_Color')] +[ComponentModel.DefaultBindingProperty('Spotlight_Color')] +[String] +$SpotlightColor, +# Set the Horizontal_Offset of OBSSpotlightShader +[Alias('Horizontal_Offset')] +[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] [Single] -$Blue, -# Set the notes of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('notes')] +$HorizontalOffset, +# Set the Vertical_Offset of OBSSpotlightShader +[Alias('Vertical_Offset')] +[ComponentModel.DefaultBindingProperty('Vertical_Offset')] +[Single] +$VerticalOffset, +# Set the Notes of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Notes')] [String] $Notes, # The name of the source. This must be provided when adding an item for the first time @@ -36388,44 +41930,62 @@ $UseShaderTime process { -$shaderName = 'gamma_correction' -$ShaderNoun = 'OBSGammaCorrectionShader' +$shaderName = 'spotlight' +$ShaderNoun = 'OBSSpotlightShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter - -uniform float Red< - string label = "Red"; +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +uniform float Speed_Percent< + string label = "Speed Percent"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; + float minimum = 0.0; + float maximum = 100.0; float step = 0.01; -> = 2.2; -uniform float Green< - string label = "Green"; +> = 100.0; +uniform float Focus_Percent< + string label = "Focus Percent"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; + float minimum = 0.0; + float maximum = 100.0; float step = 0.01; -> = 2.2; -uniform float Blue< - string label = "Blue"; +> = 15.0; +uniform bool Glitch; +uniform float4 Spotlight_Color; +uniform float Horizontal_Offset< + string label = "Horizontal Offset"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform string notes< + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Vertical_Offset< + string label = "Vertical Offset"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.5; +uniform string Notes< string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction." +> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; float4 mainImage(VertData v_in) : TARGET -{ - float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); - float4 c = image.Sample(textureSampler, v_in.uv); - c.rgb = pow(c.rgb, 1.0 / gammaRGB); - return c; +{ + float speed = Speed_Percent * 0.01; + float focus = Focus_Percent; + if (Glitch) + { + speed *= ((rand_f * 2) - 1) * 0.01; + focus *= ((rand_f * 1.1) - 0.1); + } + + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample( textureSampler, v_in.uv); + float3 lightsrc = float3(sin(elapsed_time * speed * PI * 0.667) *.5 + .5 + Horizontal_Offset, cos(elapsed_time * speed * PI) *.5 + .5 + Vertical_Offset, 1); + float3 light = normalize(lightsrc - float3( v_in.uv.x + (Horizontal_Offset * speed), v_in.uv.y + (Vertical_Offset * speed), 0)); + c0 *= pow(dot(light, float3(0, 0, 1)), focus) * Spotlight_Color; + + return c0; } ' } @@ -36524,42 +42084,40 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurAdvancedShader { +function Get-OBSSwirlShader { -[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] +[Alias('Set-OBSSwirlShader','Add-OBSSwirlShader')] param( -# Set the Directions of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Directions')] -[Single] -$Directions, -# Set the Quality of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Quality')] -[Single] -$Quality, -# Set the Size of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Size')] -[Single] -$Size, -# Set the Mask_Left of OBSGaussianBlurAdvancedShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] +# Set the radius of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('radius')] [Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurAdvancedShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] +$Radius, +# Set the angle of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('angle')] [Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurAdvancedShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] +$Angle, +# Set the center_x of OBSSwirlShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] [Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +$CenterX, +# Set the center_y of OBSSwirlShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] [Single] -$MaskBottom, +$CenterY, +# Set the animate of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the inverse of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('inverse')] +[Management.Automation.SwitchParameter] +$Inverse, +# Set the notes of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36590,109 +42148,85 @@ $UseShaderTime process { -$shaderName = 'gaussian-blur-advanced' -$ShaderNoun = 'OBSGaussianBlurAdvancedShader' +$shaderName = 'Swirl' +$ShaderNoun = 'OBSSwirlShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Directions< - string label = "Directions (16.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) -uniform float Quality< - string label = "Quality (4.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) -uniform float Size< - string label = "Size (8.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 8.0; // BLUR SIZE (Radius) -uniform float Mask_Left< - string label = "Mask left (1.0)"; +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; + float step = 0.001; +> = 0.5; // +uniform float angle< + string label = "Angle"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; + float minimum = -360.0; + float maximum = 360.0; float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; +> = 270.0; // + +uniform float center_x< + string label = "Center x"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // +uniform float center_y< + string label = "Center y"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // + +uniform bool animate = false; +uniform bool inverse = false; + +uniform string notes< + string widget_type = "info"; +> = "Distorts the screen, twisting the image in a circular motion." float4 mainImage(VertData v_in) : TARGET { - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + + v_out.uv = 1. * v_in.uv - center; - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; + float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; - c.a = transparent / count; - return c; + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv ); + } + } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36790,74 +42324,82 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurShader { +function Get-OBSTetraShader { -[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] +[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] param( -# Set the ViewProj of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the imageSize of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageSize')] -[Single[]] -$ImageSize, -# Set the imageTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageTexel')] -[Single[]] -$ImageTexel, -# Set the u_radius of OBSGaussianBlurShader -[Alias('u_radius')] -[ComponentModel.DefaultBindingProperty('u_radius')] -[Int32] -$URadius, -# Set the u_diameter of OBSGaussianBlurShader -[Alias('u_diameter')] -[ComponentModel.DefaultBindingProperty('u_diameter')] -[Int32] -$UDiameter, -# Set the u_texelDelta of OBSGaussianBlurShader -[Alias('u_texelDelta')] -[ComponentModel.DefaultBindingProperty('u_texelDelta')] -[Single[]] -$UTexelDelta, -# Set the elapsed_time of OBSGaussianBlurShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the redR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redR')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianBlurShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianBlurShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianBlurShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the kernel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernel')] -[String] -$Kernel, -# Set the kernelTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernelTexel')] -[Single[]] -$KernelTexel, -# Set the pixel_size of OBSGaussianBlurShader -[Alias('pixel_size')] -[ComponentModel.DefaultBindingProperty('pixel_size')] +$RedR, +# Set the redG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redG')] [Single] -$PixelSize, +$RedG, +# Set the redB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redB')] +[Single] +$RedB, +# Set the yelR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelR')] +[Single] +$YelR, +# Set the yelG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelG')] +[Single] +$YelG, +# Set the yelB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelB')] +[Single] +$YelB, +# Set the grnR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnR')] +[Single] +$GrnR, +# Set the grnG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnG')] +[Single] +$GrnG, +# Set the grnB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnB')] +[Single] +$GrnB, +# Set the cynR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynR')] +[Single] +$CynR, +# Set the cynG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynG')] +[Single] +$CynG, +# Set the cynB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynB')] +[Single] +$CynB, +# Set the bluR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluR')] +[Single] +$BluR, +# Set the bluG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluG')] +[Single] +$BluG, +# Set the bluB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluB')] +[Single] +$BluB, +# Set the magR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magR')] +[Single] +$MagR, +# Set the magG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magG')] +[Single] +$MagG, +# Set the magB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magB')] +[Single] +$MagB, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36888,153 +42430,203 @@ $UseShaderTime process { -$shaderName = 'gaussian-blur' -$ShaderNoun = 'OBSGaussianBlurShader' +$shaderName = 'tetra' +$ShaderNoun = 'OBSTetraShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -// OBS Default -uniform float4x4 ViewProj; +// Tetrahedral Interpolation Shader for OBS -// Settings (Shared) -uniform texture2d image; -uniform float2 imageSize; -uniform float2 imageTexel; -uniform int u_radius; -uniform int u_diameter; -uniform float2 u_texelDelta; +uniform float redR< + string label = "Red in Red"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; +uniform float redG< + string label = "Green in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -// Settings (Private) -//uniform float registerkernel[25]; -uniform texture2d kernel; -uniform float2 kernelTexel; -uniform float pixel_size = 1.0; +uniform float redB< + string label = "Blue in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -sampler_state pointClampSampler { - Filter = Point; - AddressU = Clamp; - AddressV = Clamp; -}; +uniform float yelR< + string label = "Red in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -sampler_state bilinearClampSampler { - Filter = Bilinear; - AddressU = Clamp; - AddressV = Clamp; -}; +uniform float yelG< + string label = "Green in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +uniform float yelB< + string label = "Blue in Yellow"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -float Gaussian(float x, float o) -{ - float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); -} +uniform float grnR< + string label = "Red in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -VertData VSDefault(VertData vert_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = vert_in.uv; - return vert_out; -} +uniform float grnG< + string label = "Green in Green"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel) - { - float l_gauss = Gaussian(0.0, 1.0); - float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_gauss += l_g; - } - l_value = l_value * (1.0 / l_gauss); - return l_value; -} +uniform float grnB< + string label = "Blue in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} +uniform float cynR< + string label = "Red in Cyan"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - if (pixel_size % 2 == 0) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = p_texel; - float2 l_koffset = p_kernelTexel; - for (int k = 1; k <= pixel_size; k++) { - float l_g = kernel.Sample(pointClampSampler, l_koffset).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_uvoffset += p_texel; - l_koffset += p_kernelTexel; - } - return l_value; - } else { - return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) - } -}*/ +uniform float cynG< + string label = "Green in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -float4 PSGaussian(VertData vert_in) : TARGET -{ - - float4 color = image.Sample(pointClampSampler, vert_in.uv); +uniform float cynB< + string label = "Blue in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; +uniform float bluR< + string label = "Red in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); +uniform float bluG< + string label = "Green in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - /* - return InternalGaussianPrecalculated( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ +uniform float bluB< + string label = "Blue in Blue"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - /* - return InternalGaussianPrecalculatedNVOptimize( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ +uniform float magR< + string label = "Red in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + +uniform float magG< + string label = "Green in Magenta"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float magB< + string label = "Blue in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + + +float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { + float r = RGBimage.x; + float g = RGBimage.y; + float b = RGBimage.z; + + float3 wht = float3(1.0, 1.0, 1.0); + + if (r > g) { + if (g > b) { + // r > g > b + return r * red + g * (yel - red) + b * (wht - yel); + } else if (r > b) { + // r > b > g + return r * red + g * (wht - mag) + b * (mag - red); + } else { + // b > r > g + return r * (mag - blu) + g * (wht - mag) + b * blu; + } + } else { + if (b > g) { + // b > g > r + return r * (wht - cyn) + g * (cyn - blu) + b * blu; + } else if (b > r) { + // g > b > r + return r * (wht - cyn) + g * grn + b * (cyn - grn); + } else { + // g > r > b + return r * (yel - grn) + g * grn + b * (wht - yel); + } + } } -technique Draw +float4 mainImage(VertData v_in) : TARGET { - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSGaussian(vert_in); - } + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float alpha = inputColor.a; + + float3 red = float3(redR, redG, redB); + float3 yel = float3(yelR, yelG, yelB); + float3 grn = float3(grnR, grnG, grnB); + float3 cyn = float3(cynR, cynG, cynB); + float3 blu = float3(bluR, bluG, bluB); + float3 mag = float3(magR, magG, magB); + + float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); + return float4(outputColor, alpha); } ' @@ -37134,34 +42726,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurSimpleShader { +function Get-OBSThermalShader { -[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] +[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] param( -# Set the Strength of OBSGaussianBlurSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSGaussianBlurSimpleShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurSimpleShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurSimpleShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurSimpleShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +# Set the strength of OBSThermalShader +[ComponentModel.DefaultBindingProperty('strength')] [Single] -$MaskBottom, +$Strength, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37192,101 +42764,48 @@ $UseShaderTime process { -$shaderName = 'gaussian-blur-simple' -$ShaderNoun = 'OBSGaussianBlurSimpleShader' +$shaderName = 'thermal' +$ShaderNoun = 'OBSThermalShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1.0; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; +//based on https://www.shadertoy.com/view/mdKXzG + +uniform float strength< + string label = "Strength"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; + float maximum = 200.0; + float step = 0.1; +> = 100.0; -float4 mainImage(VertData v_in) : TARGET -{ - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - - float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) - float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) - - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; - - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; +float3 heatMap(float greyValue) { + float3 heat; + heat.r = smoothstep(0.5, 0.8, greyValue); + if(greyValue >= 0.8333) { + heat.r *= (1.1 - greyValue) * 5.0; + } + if(greyValue > 0.6) { + heat.g = smoothstep(1.0, 0.7, greyValue); + } else { + heat.g = smoothstep(0.0, 0.7, greyValue); + } + heat.b = smoothstep(1.0, 0.0, greyValue); + if(greyValue <= 0.3333) { + heat.b *= greyValue / 0.3; + } + return heat; +} - c.a = transparent / count; - return c; +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float greyValue = greyScale(c.rgb); + float3 h = heatMap(greyValue*(strength/100.0)); + return float4(h.r, h.g, h.b, c.a); } ' } @@ -37385,72 +42904,26 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianExampleShader { +function Get-OBSTvCrtSubpixelShader { -[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] +[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] param( -# Set the ViewProj of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianExampleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianExampleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianExampleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSGaussianExampleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSGaussianExampleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the initial_image of OBSGaussianExampleShader -[Alias('initial_image')] -[ComponentModel.DefaultBindingProperty('initial_image')] -[String] -$InitialImage, -# Set the before_image of OBSGaussianExampleShader -[Alias('before_image')] -[ComponentModel.DefaultBindingProperty('before_image')] -[String] -$BeforeImage, -# Set the after_image of OBSGaussianExampleShader -[Alias('after_image')] -[ComponentModel.DefaultBindingProperty('after_image')] -[String] -$AfterImage, -# Set the text_color of OBSGaussianExampleShader -[Alias('text_color')] -[ComponentModel.DefaultBindingProperty('text_color')] -[String] -$TextColor, -# Set the max_distance of OBSGaussianExampleShader -[Alias('max_distance')] -[ComponentModel.DefaultBindingProperty('max_distance')] -[Single] -$MaxDistance, -# Set the exp of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('exp')] -[Single] -$Exp, +# Set the channelWidth of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelWidth')] +[Int32] +$ChannelWidth, +# Set the channelHeight of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelHeight')] +[Int32] +$ChannelHeight, +# Set the hGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('hGap')] +[Int32] +$HGap, +# Set the vGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('vGap')] +[Int32] +$VGap, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37481,193 +42954,80 @@ $UseShaderTime process { -$shaderName = 'gaussian-example' -$ShaderNoun = 'OBSGaussianExampleShader' +$shaderName = 'tv-crt-subpixel' +$ShaderNoun = 'OBSTvCrtSubpixelShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; - -/*-------------------------. -| :: Texture and sampler:: | -''-------------------------*/ - - -uniform texture2d initial_image; -sampler_state initial_sampler -{ - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = initial_image; -}; - -uniform texture2d before_image; -sampler_state before_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = before_image; -}; - -uniform texture2d after_image; -sampler_state after_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = after_image; -}; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -struct ColorData { - float4 initial_color : SV_TARGET0; - float4 before_color: SV_TARGET1; - float4 after_color : SV_TARGET2; -}; - -uniform float4 text_color; -uniform float max_distance; -uniform float exp; - -#define PI 3.141592653589793238462643383279502884197169399375105820974 - -VertData mainTransform(VertData v_in) -{ - VertData vert_out = v_in; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} +// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro -float4 grayscale(float4 color) -{ - float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; - return float4(grayscale, grayscale, grayscale, color.a); -} +// width of a single color channel in pixels +uniform int channelWidth< + string label = "Channel Width"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; -float4 gaussian(VertData v_in, float angle) -{ - float rad = radians(angle); - float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); - float2 dir_2 = dir * 2.0; - float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; - - float4 px_away = image.Sample(textureSampler, v_in.uv + dir); - px_away += image.Sample(textureSampler, v_in.uv - dir); - px_away *= 0.25; - - float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away *= 0.0625; - - return ret + px_away + px_2_away; -} +// height of color channels in pixels +uniform int channelHeight< + string label = "Channel Height"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 3; -ColorData setColorData(VertData v_in): SV_TARGET0 -{ - //string RenderTarget0 = "initial_image"; - ColorData cd;// = (ColorData)0; - cd.initial_color = image.Sample(textureSampler, v_in.uv); - cd.before_color = float4(0.0,0.0,1.0,1.0); - cd.after_color = float4(1.0,0.0,0.0,1.0); - return cd; -} +// horizontal distance between two neighboring pixels +uniform int hGap< + string label = "Horizontal Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; -float4 blurImageH(VertData v_in) : SV_TARGET1 -{ - //string RenderTarget1 = "before_image"; - //ColorData cd = (ColorData)0; - //cd.initial_color = image.Sample(textureSampler, v_in.uv); - //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); - return float4(0.0,0.0,1.0,1.0); -} +// vertical distance between two neighboring pixels +uniform int vGap< + string label = "Vertical Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; -float4 blurImageV(VertData v_in) : SV_TARGET2 +float4 mainImage(VertData v_in) : TARGET { - //string RenderTarget2 = "after_image"; - //ColorData cd = (ColorData)0; - //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); - return float4(1.0,0.0,0.0,1.0); -} - -float4 mainImage(VertData v_in) : SV_TARGET0 -{ - float4 color; - ColorData cd;// = (ColorData)0; - - //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); - //cd.before_color = before_image.Sample(before_sampler, v_in.uv); - cd.after_color = after_image.Sample(before_sampler, v_in.uv); - - if (max_distance <= 5) { - color = cd.before_color; - } - else { - color = cd.after_color;//image.Sample(textureSampler, v_in.uv); - } - - float4 gray = grayscale(color); - float4 gray_text = grayscale(text_color); - float d = distance(gray.rgb, gray_text.rgb); - if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ - float d_c = pow(d*2, exp) / pow(2, exp); - d_c = sin(d_c * PI / 2); - d = pow(1.0 - sin(d * PI/4), exp); - - color.rgb = float3(d,d,d); - } - - return color; -} + float columns = float(channelWidth * 3 + hGap); + float pixelHeight = float(channelHeight + vGap); -technique Draw -{ - pass pre - { - vertex_shader = mainTransform(v_in); - pixel_shader = setColorData(v_in); - } + float2 fragCoord = v_in.uv * uv_size; + float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); + float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); + float2 sampleUv = pixel / sampleRes; - pass b0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageH(v_in); - } + // color of sample point + float4 col = image.Sample(textureSampler, sampleUv); + + int column = int(fragCoord.x) % (channelWidth * 3 + hGap); - pass b1 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageV(v_in); - } + // set color based on which channel this fragment corresponds to + if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); + else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); + else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); + else col = float4(0.0, 0.0, 0.0, col.a); - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } + // offset every other column of pixels + int height = int(pixelHeight); + if (int(pixel.x) % 2 == 0) { + if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); + } else { + if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); + } + // Output to screen + return col; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -37765,75 +43125,28 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianSimpleShader { +function Get-OBSTwistShader { -[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] +[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] param( -# Set the ViewProj of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianSimpleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianSimpleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianSimpleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianSimpleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSGaussianSimpleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSGaussianSimpleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSGaussianSimpleShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSGaussianSimpleShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSGaussianSimpleShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the samples of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('samples')] +# Set the center_x_percent of OBSTwistShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] [Int32] -$Samples, -# Set the LOD of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('LOD')] +$CenterXPercent, +# Set the center_y_percent of OBSTwistShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] [Int32] -$LOD, +$CenterYPercent, +# Set the power of OBSTwistShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the rotation of OBSTwistShader +[ComponentModel.DefaultBindingProperty('rotation')] +[Single] +$Rotation, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37864,102 +43177,61 @@ $UseShaderTime process { -$shaderName = 'gaussian-simple' -$ShaderNoun = 'OBSGaussianSimpleShader' +$shaderName = 'twist' +$ShaderNoun = 'OBSTwistShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/ltScRG Converted inspiration +uniform int center_x_percent< + string label = "center x percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.3; +uniform float rotation< + string label = "rotation"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.001; +> = 2.0; -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 +#ifndef OPENGL #define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float +#endif -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ - -// 16x acceleration of https://www.shadertoy.com/view/4tSyzy -// by applying gaussian at intermediate MIPmap level. - -uniform int samples< - string label = "Samples"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 25; - int step = 1; -> = 16; -uniform int LOD< - string label = "LOD"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 2; // gaussian done on MIPmap at scale LOD - -float gaussian(vec2 i) -{ - float sigma = (float(samples) * .25); - return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); -} - -vec4 blur(vec2 U, vec2 scale) -{ - vec4 O = vec4(0,0,0,0); - int sLOD = (1 << LOD); // tile size = 2^LOD - int s = samples / sLOD; - - for (int i = 0; i < s * s; i++) - { - vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; - O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); - //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); - //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); - } - - return O / O.a; +mat2 rotate(float angle){ + return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); } float4 mainImage(VertData v_in) : TARGET { - float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; - //float2 iResolution = 1 - v_in.uv + 1.0; - //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); - return blur(v_in.uv / iResolution, 1.0 / iResolution); - //return rgba; + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float d = distance(center_pos,v_in.uv); + if(d > power){ + return image.Sample(textureSampler, v_in.uv); + } + float r = (cos(d*3.14159265359/power) +1)/2 * rotation; + float2 pos = v_in.uv - center_pos; + pos = mul(pos, rotate(r)); + pos += center_pos; + return image.Sample(textureSampler, pos); } - - - - - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -38057,56 +43329,73 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGbCameraShader { +function Get-OBSTwoPassDropShadowShader { -[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] +[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] param( -# Set the pixelSize of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('pixelSize')] -[Single] -$PixelSize, -# Set the dither_factor of OBSGbCameraShader -[Alias('dither_factor')] -[ComponentModel.DefaultBindingProperty('dither_factor')] -[Single] -$DitherFactor, -# Set the alternative_bayer of OBSGbCameraShader -[Alias('alternative_bayer')] -[ComponentModel.DefaultBindingProperty('alternative_bayer')] -[Management.Automation.SwitchParameter] -$AlternativeBayer, -# Set the brightness of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('brightness')] -[Single] -$Brightness, -# Set the contrast of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('contrast')] +# Set the ViewProj of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSTwoPassDropShadowShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$Contrast, -# Set the gamma of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('gamma')] +$ElapsedTime, +# Set the uv_offset of OBSTwoPassDropShadowShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSTwoPassDropShadowShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSTwoPassDropShadowShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSTwoPassDropShadowShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$Gamma, -# Set the color_1 of OBSGbCameraShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the color_2 of OBSGbCameraShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the color_3 of OBSGbCameraShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the color_4 of OBSGbCameraShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] +$RandF, +# Set the uv_size of OBSTwoPassDropShadowShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the shadow_offset_x of OBSTwoPassDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSTwoPassDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSTwoPassDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the shadow_color of OBSTwoPassDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] [String] -$Color4, +$ShadowColor, +# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader +[Alias('is_alpha_premultiplied')] +[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] +[Management.Automation.SwitchParameter] +$IsAlphaPremultiplied, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38137,164 +43426,126 @@ $UseShaderTime process { -$shaderName = 'gb-camera' -$ShaderNoun = 'OBSGbCameraShader' +$shaderName = 'two-pass-drop-shadow' +$ShaderNoun = 'OBSTwoPassDropShadowShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * ------------------------------------------------------------ - * "THE BEERWARE LICENSE" (Revision 42): - * maple wrote this code. As long as you retain this - * notice, you can do whatever you want with this stuff. If we - * meet someday, and you think this stuff is worth it, you can - * buy me a beer in return. - * ------------------------------------------------------------ - * from https://www.shadertoy.com/view/3tSXRh - * adopted for OBS by Exeldro - * ------------------------------------------------------------ - */ +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float pixelSize< - string label = "Pixel Size"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 50.0; - float step = 0.1; -> = 3.0; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; -uniform float dither_factor< - string label = "Dither Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; -uniform bool alternative_bayer; +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -uniform float brightness< - string label = "Brightness"; +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} + +uniform int shadow_offset_x< + string label = "shadow offset x"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float contrast< - string label = "Contrast"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_offset_y< + string label = "shadow offset y"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float gamma< - string label = "Gamma"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.6; - -uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; -uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; -uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; -uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; - -// quantize coords to low resolution -float2 pixelize(float2 uv, float2 pixelSize) { - float2 factor = pixelSize / uv_size; - return floor(uv / factor) * factor; -} + int minimum = 0; + int maximum = 100; + int step = 1; +>; -float3 colorLUT(float3 color) { - float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; - if(gray < 0.25) - return color_1.rgb; - if(gray < 0.50) - return color_2.rgb; - if(gray < 0.75) - return color_3.rgb; - return color_4.rgb; -} +uniform float4 shadow_color; -// adjust brightness, contrast and gamma levels of a color -float3 levels(float3 color, float brightness, float contrast, float3 gamma) { - float3 value = (color - 0.5) * contrast + 0.5; - value = clamp(value + brightness, 0.0, 1.0); - return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); -} -float3 levels(float3 color, float brightness, float contrast, float gamma) { - return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); -} +uniform bool is_alpha_premultiplied; -// applies the dithering filter to a color map -float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { - // reduces pixel space to the selected pixel size - float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); - - // applies the bayer matrix filter to the color map - pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); - int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); - float bayer; - if (alternative_bayer){ -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ - 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ - 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ - 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ - 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ - 51, 19, 59, 27, 49, 17, 57, 25, - 15, 47, 7, 39, 13, 45, 5, 37, - 63, 31, 55, 23, 61, 29, 53, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; - } else { -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 48, 12, 60, 3, 51, 15, 63, - 32, 16, 44, 28, 35, 19, 47, 31, - 8, 56, 4, 52, 11, 59, 7, 55, - 40, 24, 36, 20, 43, 27, 39, 23, - 2, 50, 14, 62, 1, 49, 13, 61, - 34, 18, 46, 30, 33, 17, 45, 29, - 10, 58, 6, 54, 9, 57, 5, 53, - 42, 26, 38, 22, 41, 25, 37, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float sampled_shadow_alpha = 0; + + for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; } - float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); - // limits it to the selected palette - color = colorLUT(bayerColor); - - return color; + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); } -float4 mainImage(VertData v_in) : TARGET +float4 mainImage_2_end(VertData v_in) : TARGET { - float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); - texcoord = clamp(texcoord, 0.001, 1.0); - float4 c = image.Sample(textureSampler, texcoord); - float3 color = c.rgb; - - color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); + int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); - color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, + v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); - return float4(color.r, color.g, color.b, c.a); + float sampled_shadow_alpha = 0; + + for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) + { + float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; + } + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); +} + +technique Draw +{ + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } + + pass p1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage_2_end(v_in); + } } ' @@ -38394,57 +43645,37 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlassShader { +function Get-OBSVCRShader { -[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] +[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] param( -# Set the Alpha_Percent of OBSGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +# Set the vertical_shift of OBSVCRShader +[Alias('vertical_shift')] +[ComponentModel.DefaultBindingProperty('vertical_shift')] [Single] -$AlphaPercent, -# Set the Offset_Amount of OBSGlassShader -[Alias('Offset_Amount')] -[ComponentModel.DefaultBindingProperty('Offset_Amount')] +$VerticalShift, +# Set the distort of OBSVCRShader +[ComponentModel.DefaultBindingProperty('distort')] [Single] -$OffsetAmount, -# Set the xSize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('xSize')] -[Int32] -$XSize, -# Set the ySize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('ySize')] -[Int32] -$YSize, -# Set the Reflection_Offset of OBSGlassShader -[Alias('Reflection_Offset')] -[ComponentModel.DefaultBindingProperty('Reflection_Offset')] -[Int32] -$ReflectionOffset, -# Set the Horizontal_Border of OBSGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] +$Distort, +# Set the vignet of OBSVCRShader +[ComponentModel.DefaultBindingProperty('vignet')] [Single] -$BorderOffset, -# Set the Border_Color of OBSGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the Glass_Color of OBSGlassShader -[Alias('Glass_Color')] -[ComponentModel.DefaultBindingProperty('Glass_Color')] -[String] -$GlassColor, -# Set the notes of OBSGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$Vignet, +# Set the stripe of OBSVCRShader +[ComponentModel.DefaultBindingProperty('stripe')] +[Single] +$Stripe, +# Set the vertical_factor of OBSVCRShader +[Alias('vertical_factor')] +[ComponentModel.DefaultBindingProperty('vertical_factor')] +[Single] +$VerticalFactor, +# Set the vertical_height of OBSVCRShader +[Alias('vertical_height')] +[ComponentModel.DefaultBindingProperty('vertical_height')] +[Single] +$VerticalHeight, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38475,95 +43706,108 @@ $UseShaderTime process { -$shaderName = 'glass' -$ShaderNoun = 'OBSGlassShader' +$shaderName = 'VCR' +$ShaderNoun = 'OBSVCRShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 -uniform float Alpha_Percent< - string label = "Alpha Percent"; +//based on https://www.shadertoy.com/view/ldjGzV +//Converted to OpenGL by Exeldro February 19, 2022 +uniform float vertical_shift< + string label = "vertical shift"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Offset_Amount< - string label = "Offset Amount"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.4; +uniform float distort< + string label = "distort"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; -uniform int xSize< - string label = "x Size"; + float minimum = 0; + float maximum = 5.0; + float step = 0.001; +> = 1.2; +uniform float vignet< + string label = "vignet"; string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int ySize< - string label = "y Size"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float stripe< + string label = "stripe"; string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int Reflection_Offset< - string label = "Reflection Offset"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_factor< + string label = "vertical factor"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 2; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_height< + string label = "vertical height"; string widget_type = "slider"; - float minimum = -0.01; - float maximum = 1.01; - float step = 0.01; -> = 0.5; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform float4 Glass_Color; -uniform string notes< - string widget_type = "info"; -> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 30.0; -float mod(float a, float b){ - float d = a / b; - return (d-floor(d))*b; +float onOff(float a, float b, float c) +{ + return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); } -float4 mainImage(VertData v_in) : TARGET +float ramp(float y, float start, float end) { + float inside = step(start,y) - step(end,y); + float fact = (y-start)/(end-start)*inside; + return (1.-fact) * inside; +} - int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); - int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); - float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); - float2 uv = v_in.uv + offsets; - float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} - float4 rgba = image.Sample(textureSampler, v_in.uv); - float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); - rgba = image.Sample(textureSampler, uv); - float4 rgba_glass = image.Sample(textureSampler, uv2); - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; +float stripes(float2 uv) +{ + return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; +} - if (uv_compare < (Border_Offset - 0.005)) - { - rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - rgba_output = image.Sample(textureSampler, v_in.uv); - } - return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); +float4 getVideo(float2 uv) +{ + float2 look = uv; + float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); + look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; + float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + + (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); + look.y = modu((look.y + vShift) , 1.); + return image.Sample(textureSampler, look); +} + +float2 screenDistort(float2 uv) +{ + uv -= float2(.5,.5); + uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); + uv += float2(.5,.5); + return uv; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv = screenDistort(uv); + float4 video = getVideo(uv); + float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); + float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; + video += stripes(uv); + video *= vignette; + video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; + return float4(video.r, video.g, video.b ,1.0); } ' @@ -38663,84 +43907,55 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchAnalogShader { +function Get-OBSVHSShader { -[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] +[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] param( -# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader -[Alias('scan_line_jitter_displacement')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] +# Set the range of OBSVHSShader +[ComponentModel.DefaultBindingProperty('range')] [Single] -$ScanLineJitterDisplacement, -# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader -[Alias('scan_line_jitter_threshold_percent')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] -[Int32] -$ScanLineJitterThresholdPercent, -# Set the vertical_jump_amount of OBSGlitchAnalogShader -[Alias('vertical_jump_amount')] -[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] +$Range, +# Set the offsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('offsetIntensity')] [Single] -$VerticalJumpAmount, -# Set the vertical_speed of OBSGlitchAnalogShader -[Alias('vertical_speed')] -[ComponentModel.DefaultBindingProperty('vertical_speed')] +$OffsetIntensity, +# Set the noiseQuality of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseQuality')] [Single] -$VerticalSpeed, -# Set the horizontal_shake of OBSGlitchAnalogShader -[Alias('horizontal_shake')] -[ComponentModel.DefaultBindingProperty('horizontal_shake')] +$NoiseQuality, +# Set the noiseIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseIntensity')] [Single] -$HorizontalShake, -# Set the color_drift_amount of OBSGlitchAnalogShader -[Alias('color_drift_amount')] -[ComponentModel.DefaultBindingProperty('color_drift_amount')] +$NoiseIntensity, +# Set the colorOffsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] [Single] -$ColorDriftAmount, -# Set the color_drift_speed of OBSGlitchAnalogShader -[Alias('color_drift_speed')] -[ComponentModel.DefaultBindingProperty('color_drift_speed')] +$ColorOffsetIntensity, +# Set the Alpha_Percentage of OBSVHSShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] [Single] -$ColorDriftSpeed, -# Set the pulse_speed_percent of OBSGlitchAnalogShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the alpha_percent of OBSGlitchAnalogShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] -[Int32] -$AlphaPercent, -# Set the rotate_colors of OBSGlitchAnalogShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +$AlphaPercentage, +# Set the Apply_To_Image of OBSVHSShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] [Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Replace_Image_Color of OBSGlitchAnalogShader +$ApplyToImage, +# Set the Replace_Image_Color of OBSVHSShader [Alias('Replace_Image_Color')] [ComponentModel.DefaultBindingProperty('Replace_Image_Color')] [Management.Automation.SwitchParameter] $ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGlitchAnalogShader +# Set the Color_To_Replace of OBSVHSShader [Alias('Color_To_Replace')] [ComponentModel.DefaultBindingProperty('Color_To_Replace')] [String] $ColorToReplace, -# Set the notes of OBSGlitchAnalogShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +# Set the Apply_To_Specific_Color of OBSVHSShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38771,152 +43986,135 @@ $UseShaderTime process { -$shaderName = 'glitch_analog' -$ShaderNoun = 'OBSGlitchAnalogShader' +$shaderName = 'VHS' +$ShaderNoun = 'OBSVHSShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float scan_line_jitter_displacement< - string label = "Scan line jitter"; +//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 +//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 +//Converted to OpenGL by Exeldro February 19, 2022 +//Use improved input fields by Exeldro April 15, 2023 +uniform float range< + string label = "Wave size (0.05)"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 0.20; float step = 0.01; -> = 33.0; // (displacement, threshold) -uniform int scan_line_jitter_threshold_percent< - string label = "scan line jitter threshold percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 95; -uniform float vertical_jump_amount< - string label = "Vertical jump amount"; +> = 0.05; +uniform float offsetIntensity< + string label = "Offset intensity (0.02)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; + float minimum = 0.01; + float maximum = 0.20; float step = 0.01; ->; -uniform float vertical_speed< - string label = "Vertical speed"; +> = 0.02; +uniform float noiseQuality< + string label = "Noise number of lines (250)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform float horizontal_shake< - string label = "Horizontal shake"; + float minimum = 1.0; + float maximum = 1000.0; + float step = 10.0; +> = 250.0; +uniform float noiseIntensity< + string label = "Noise intensity (0.88)"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 10.0; float step = 0.01; ->; -uniform float color_drift_amount< - string label = "Color drift amount"; +> = 0.88; +uniform float colorOffsetIntensity< + string label = "Color offset intensity (1.3)"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float color_drift_speed< - string label = "Color drift speed"; + float maximum = 10.0; + float step = 0.1; +> = 1.3; +uniform float Alpha_Percentage< + string label = "Aplha percentage (100.0)"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int alpha_percent< - string label = "Aplha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = false; + float step = 1.0; +> = 100.0; +uniform bool Apply_To_Image; uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; uniform float4 Color_To_Replace; -uniform string notes< - string widget_type = "info"; -> ="play with settings!"; +uniform bool Apply_To_Specific_Color; +float dot2(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; +} -float nrand(float x, float y) +float rand(float2 co) { - float value = dot(float2(x, y), float2(12.9898 , 78.233 )); - return frac(sin(value) * 43758.5453); + return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); } -float4 mainImage(VertData v_in) : TARGET +float verticalBar(float pos, float uvY, float offset) { - float speed = pulse_speed_percent * 0.01; - float alpha = alpha_percent * 0.01; - float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; - float u = v_in.uv.x; - float v = v_in.uv.y; - float t = sin(elapsed_time * speed) * 2 - 1; - float4 rgba = image.Sample(textureSampler, v_in.uv); + float edge0 = (pos - range); + float edge1 = (pos + range); - // Scan line jitter - float jitter = nrand(v, t) * 2 - 1; - jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; + float x = smoothstep(edge0, pos, uvY) * offset; + x -= smoothstep(pos, edge1, uvY) * offset; + return x; +} - // Vertical jump - float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} - // Horizontal shake - float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; +float dot4(float4 a,float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} - //// Color drift - float drift = sin(jump + color_drift_speed) * color_drift_amount; +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + for (float i = 0.0; i < 0.71; i += 0.1313) + { + float d = modu(elapsed_time * i, 1.7); + float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); + o *= offsetIntensity; + uv.x += verticalBar(d, uv.y, o); + } + float uvY = uv.y; + uvY *= noiseQuality; + uvY = float(int(uvY)) * (1.0 / noiseQuality); + float noise = rand(float2(elapsed_time * 0.00001, uvY)); + uv.x += noise * noiseIntensity / 100.0; - float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); - float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); - - if(rotate_colors) - { - // get general time number between 0 and 4 - float tx = (t + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls - //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 - src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); - //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 - src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); - //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 - src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); - - } + float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; + float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; - float4 color = rgba; - float4 original_color = color; - rgba = float4(src1.x, src2.x, src1.y, alpha); + float4 rgba = image.Sample(textureSampler, uv); + float r = image.Sample(textureSampler, uv + offsetR).r; + float g = image.Sample(textureSampler, uv + offsetG).g; + float b = rgba.b; - if (Apply_To_Alpha_Layer) + rgba = float4(r, g, b, rgba.a); + + float4 color; + float4 original_color; + if (Apply_To_Image) { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color, alpha); + color = float4(luma,luma,luma,luma); + rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } - if (Apply_To_Specific_Color) { - color = original_color; + color = image.Sample(textureSampler, v_in.uv); + original_color = color; color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); } - + return rgba; } @@ -39017,18 +44215,26 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchShader { +function Get-OBSVignettingShader { -[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] +[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] param( -# Set the AMT of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('AMT')] +# Set the innerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('innerRadius')] [Single] -$AMT, -# Set the SPEED of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('SPEED')] +$InnerRadius, +# Set the outerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('outerRadius')] [Single] -$SPEED, +$OuterRadius, +# Set the opacity of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# Set the notes of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39059,82 +44265,62 @@ $UseShaderTime process { -$shaderName = 'glitch' -$ShaderNoun = 'OBSGlitchShader' +$shaderName = 'vignetting' +$ShaderNoun = 'OBSVignettingShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MtXBDs -//inputs -uniform float AMT< - string label = "AMT"; +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +uniform float innerRadius< + string label = "inner radius"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.2; //0 - 1 glitch amount -uniform float SPEED< - string label = "Speed"; + float maximum = 5.0; + float step = 0.001; +> = 0.9; +uniform float outerRadius< + string label = "outer radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.5; +uniform float opacity< + string label = "opacity"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; - float step = 0.01; -> = 0.6; //0 - 1 speed - -//2D (returns 0 - 1) -float random2d(float2 n) { - return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); -} - -float randomRange (in float2 seed, in float min, in float max) { - return min + random2d(seed) * (max - min); -} - -// return 1 if v inside 1d range -float insideRange(float v, float bottom, float top) { - return step(bottom, v) - step(top, v); -} - + float step = 0.001; +> = 0.8; +uniform string notes< + string widget_type = "info"; +> = "inner radius will always be shown, outer radius is the falloff"; - float4 mainImage(VertData v_in) : TARGET { - - float time = floor(elapsed_time * SPEED * 60.0); - float2 uv = v_in.uv; - - //copy orig - float4 outCol = image.Sample(textureSampler, uv); - - //randomly offset slices horizontally - float maxOffset = AMT/2.0; - for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { - float sliceY = random2d(float2(time , 2345.0 + float(i))); - float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; - float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); - float2 uvOff = uv; - uvOff.x += hOffset; - if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ - outCol = image.Sample(textureSampler, uvOff); - } - } - - //do slight offset on one entire channel - float maxColOffset = AMT/6.0; - float rnd = random2d(float2(time , 9545.0)); - float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), - randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); - if (rnd < 0.33){ - outCol.r = image.Sample(textureSampler, uv + colOffset).r; - - }else if (rnd < 0.66){ - outCol.g = image.Sample(textureSampler, uv + colOffset).g; - - } else{ - outCol.b = image.Sample(textureSampler, uv + colOffset).b; - } - - return outCol; + float PI = 3.1415926535897932384626433832795;//acos(-1); + + float4 c0 = image.Sample(textureSampler, v_in.uv); + float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; + + float xTrans = (v_in.uv.x * 2) - 1; + float yTrans = 1 - (v_in.uv.y * 2); + + float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); + + float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); + float factor = 1 - subtraction; + + float4 vignetColor = c0 * factor; + vignetColor *= verticalDim; + + vignetColor *= opacity; + c0 *= 1-opacity; + + float4 output_color = c0 + vignetColor; + + return float4(output_color); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -39232,42 +44418,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlowShader { +function Get-OBSVoronoiPixelationShader { -[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] +[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] param( -# Set the glow_percent of OBSGlowShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSGlowShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSGlowShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSGlowShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed of OBSGlowShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] -[Int32] -$PulseSpeed, -# Set the ease of OBSGlowShader -[ComponentModel.DefaultBindingProperty('ease')] +# Set the pixH of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('pixH')] +[Single] +$PixH, +# Set the alternative of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('alternative')] [Management.Automation.SwitchParameter] -$Ease, -# Set the notes of OBSGlowShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$Alternative, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39298,105 +44460,99 @@ $UseShaderTime process { -$shaderName = 'glow' -$ShaderNoun = 'OBSGlowShader' +$shaderName = 'voronoi-pixelation' +$ShaderNoun = 'OBSVoronoiPixelationShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int glow_percent< - string label = "Glow percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform string notes< - string widget_type = "info"; -> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; +// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro +uniform float pixH< + string label = "Size"; + string widget_type = "slider"; + float minimum = 4.0; + float maximum = 500.0; + float step = 0.01; +> = 100.0; +uniform bool alternative; -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); } -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; +float2 random2( float2 p ) { + return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); +} +float2 randomSpin(float2 p, float f){ + return 1.0 * float2( + cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), + sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); } +float4 VoronoiPixelation(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); -float4 mainImage(VertData v_in) : TARGET -{ - float2 offsets[4]; - offsets[0] = float2(-0.1, 0.125); - offsets[1] = float2(-0.1, -0.125); - offsets[2] = float2(0.1, -0.125); - offsets[3] = float2(0.1, 0.125); - // convert input for vector math - float4 col = image.Sample(textureSampler, v_in.uv); - float blur_amount = float(blur) /100.0; - float glow_amount = float(glow_percent) * 0.01; - float speed = float(pulse_speed) * 0.01; - float luminance_floor = float(min_brightness) /100.0; - float luminance_ceiling = float(max_brightness) /100.0; + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); - if (col.a > 0.0) - { - //circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration + float2 point1 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point1 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; + } + } + } + float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x ; + + return image.Sample(textureSampler, nPoint); +} +float4 VoronoiPixelation2(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); - // simple glow calc - for (int n = 0; n < 4; n++) { - b = BlurStyler(t, 0, c, d, ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - col += (ncolor * (glow_amount * b)); - } - } - } - return col; + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); + + float2 point2 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point2 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; + } + } + } + float2 nPoint = pixExt + relClos + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x; + + return image.Sample(textureSampler, nPoint); } + +float4 mainImage(VertData v_in) : TARGET +{ + if (alternative) { + return VoronoiPixelation2(v_in.uv, pixH); + } else { + return VoronoiPixelation(v_in.uv, pixH); + } +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -39494,96 +44650,59 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGradientShader { +function Get-OBSWalkingDeadPixelFixerShader { -[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] +[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] param( -# Set the start_color of OBSGradientShader -[Alias('start_color')] -[ComponentModel.DefaultBindingProperty('start_color')] -[String] -$StartColor, -# Set the start_step of OBSGradientShader -[Alias('start_step')] -[ComponentModel.DefaultBindingProperty('start_step')] -[Single] -$StartStep, -# Set the middle_color of OBSGradientShader -[Alias('middle_color')] -[ComponentModel.DefaultBindingProperty('middle_color')] -[String] -$MiddleColor, -# Set the middle_step of OBSGradientShader -[Alias('middle_step')] -[ComponentModel.DefaultBindingProperty('middle_step')] -[Single] -$MiddleStep, -# Set the end_color of OBSGradientShader -[Alias('end_color')] -[ComponentModel.DefaultBindingProperty('end_color')] -[String] -$EndColor, -# Set the end_step of OBSGradientShader -[Alias('end_step')] -[ComponentModel.DefaultBindingProperty('end_step')] -[Single] -$EndStep, -# Set the alpha_percent of OBSGradientShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] +# Set the Scan_Width of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Width')] +[ComponentModel.DefaultBindingProperty('Scan_Width')] [Int32] -$AlphaPercent, -# Set the pulse_speed of OBSGradientShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] +$ScanWidth, +# Set the Scan_Height of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Height')] +[ComponentModel.DefaultBindingProperty('Scan_Height')] [Int32] -$PulseSpeed, -# Set the ease of OBSGradientShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the rotate_colors of OBSGradientShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the horizontal of OBSGradientShader -[ComponentModel.DefaultBindingProperty('horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the vertical of OBSGradientShader -[ComponentModel.DefaultBindingProperty('vertical')] +$ScanHeight, +# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_X')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] +[Int32] +$ScanOffsetX, +# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] +[Int32] +$ScanOffsetY, +# Set the Show_Border of OBSWalkingDeadPixelFixerShader +[Alias('Show_Border')] +[ComponentModel.DefaultBindingProperty('Show_Border')] [Management.Automation.SwitchParameter] -$Vertical, -# Set the gradient_center_width_percentage of OBSGradientShader -[Alias('gradient_center_width_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] +$ShowBorder, +# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader +[Alias('Contrast_Threshold')] +[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] +[Single] +$ContrastThreshold, +# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Min_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] [Int32] -$GradientCenterWidthPercentage, -# Set the gradient_center_height_percentage of OBSGradientShader -[Alias('gradient_center_height_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] +$MinClusterSize, +# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Max_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] [Int32] -$GradientCenterHeightPercentage, -# Set the notes of OBSGradientShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$MaxClusterSize, +# Set the Show_Green of OBSWalkingDeadPixelFixerShader +[Alias('Show_Green')] +[ComponentModel.DefaultBindingProperty('Show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the Bypass of OBSWalkingDeadPixelFixerShader +[ComponentModel.DefaultBindingProperty('Bypass')] +[Management.Automation.SwitchParameter] +$Bypass, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39614,3410 +44733,5317 @@ $UseShaderTime process { -$shaderName = 'gradient' -$ShaderNoun = 'OBSGradientShader' +$shaderName = 'walking-dead-pixel-fixer' +$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; -uniform float start_step< - string label = "Start step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.15; -uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; -uniform float middle_step< - string label = "Middle step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.4; -uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; -uniform float end_step< - string label = "End step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform int alpha_percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; +// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter +// by Eegee http://github.com/eegee/ +// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixels located in the user configured scan area and passing the threshold settings will have its colors +// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. +// +// Changelog: +// 0.01 - Initial release +// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. + +uniform int Scan_Width< + string label = "Scan area width"; + int minimum = 1; + int maximum = 2560; int step = 1; -> = 90; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; +> = 75; + +uniform int Scan_Height< + string label = "Scan area height"; + int minimum = 1; + int maximum = 1440; int step = 1; -> = 0; -uniform bool ease; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = true; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform bool horizontal; -uniform bool vertical; -uniform int gradient_center_width_percentage< - string label = "gradient center width percentage"; - string widget_type = "slider"; +> = 120; + +uniform int Scan_Offset_X< + string label = "Scan area offset X"; int minimum = 0; - int maximum = 100; + int maximum = 2560; int step = 1; -> = 50; -uniform int gradient_center_height_percentage< - string label = "gradient center height percentage"; - string widget_type = "slider"; +> = 110; + +uniform int Scan_Offset_Y< + string label = "Scan area offset Y"; int minimum = 0; - int maximum = 100; + int maximum = 1440; int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; - -float EaseInOutCircTimer(float t, float b, float c, float d) { - t /= d / 2; - if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; - t -= 2; - return c / 2 * (sqrt(1 - t * t) + 1) + b; -} +> = 20; -float BlurStyler(float t, float b, float c, float d, bool ease) -{ - if (ease) return EaseInOutCircTimer(t, 0, c, d); - return t; -} +uniform bool Show_Border< + string label = "Show scan area border in red"; + string widget_type = "checkbox"; +> = true; -struct gradient +uniform float Contrast_Threshold< + string label = "Contrast threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; + +uniform int Min_Cluster_Size< + string label = "Min cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 324; + +uniform int Max_Cluster_Size< + string label = "Max cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 400; + +uniform bool Show_Green< + string label = "Show matches in green"; + string widget_type = "checkbox"; +> = true; + +uniform bool Bypass< + string label = "Bypass"; + string widget_type = "checkbox"; +> = false; + + +#define SAMPLE_RADIUS 9 + +float luminance(float3 color) { - float4 color; - float step; -}; + return dot(color, float3(0.299, 0.587, 0.114)); +} + +void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) +{ + float3 sumColor = float3(0.0, 0.0, 0.0); + float weightSum = 0.0; + contrastCount = 0; + + float3 centerColor = image.Sample(textureSampler, uv).rgb; + float centerLum = luminance(centerColor); + + for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) + { + for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) + { + if (x == 0 && y == 0) continue; + + float2 offset = float2(x, y) / uv_size; + float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); + + // skip central pixel + if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && + ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) + continue; + + float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; + float lum = luminance(sampleColor); + + float weight = 1.0; + sumColor += sampleColor * weight; + weightSum += weight; + + if (abs(lum - centerLum) >= contrastThreshold) + contrastCount++; + } + } + if (weightSum > 0) + { + avgColor = sumColor / weightSum; + } + else + { + avgColor = centerColor; + } + avgLuminance = luminance(avgColor); +} float4 mainImage(VertData v_in) : TARGET { - const float PI = 3.14159265f;//acos(-1); - float speed = float(pulse_speed) * 0.01; - float alpha = float(alpha_percent) * 0.01; - - //circular easing variable - float t = sin(elapsed_time * speed) * 2 - 1; - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - - float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); - float4 color = image.Sample(textureSampler, v_in.uv); - float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; - float4 gray = float4(luminance,luminance,luminance, 1); + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - b = BlurStyler(t, 0, c, d, ease); + float4 tex = image.Sample(textureSampler, uv); + float3 color = tex.rgb; - const int no_colors = 3; - float4 s_color = start_color; - float4 m_color = middle_color; - float4 e_color = end_color; + if (!Bypass) + { + int pixX = (int)round(pos.x); + int pixY = (int)round(pos.y); - if (rotate_colors) - { - // get general time number between 0 and 4 - float tx = (b + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls + int borderwidth = 2; - if (tx <= 2.0) - { - s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - } + bool insideScan = + (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && + (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); - if ((tx >= 1.0) && (tx <= 3.0)) - { - s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - } + bool borderingScan = + (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && + (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); - if (tx >= 2.0) - { - s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - } + if (insideScan) + { + float3 avgColor; + float avgLum; + int contrastCount; - if (tx < 0) - { - s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - } - } + sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); - float4 colors[no_colors]; - colors[0] =s_color; - colors[1] = m_color; - colors[2] = e_color; - float step[no_colors]; - step[0] = start_step; - step[1] = middle_step; - step[2] = end_step; + if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) + { + color = avgColor; - float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); - float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); - float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); + if (Show_Green) + { + int left = pixX - borderwidth; + int right = pixX + borderwidth; + int top = pixY - borderwidth; + int bottom = pixY + borderwidth; - float dist = distance(v_in.uv, gradient_center); - if (horizontal && (vertical == false)) - { - dist = distance(v_in.uv.y, gradient_center.y); - } - if (vertical && (horizontal == false)) - { - dist = distance(v_in.uv.x, gradient_center.x); - } + bool onOutline = + abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || + abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; - float4 col = colors[0]; - for (int i = 1; i < no_colors; ++i) { - col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); - } - col.a = clamp(alpha, 0.0, 1.0); - if (Apply_To_Alpha_Layer == false) - color.a = alpha; - if (Apply_To_Specific_Color) + if (onOutline) + return float4(0.0, 1.0, 0.0, 1.0); + } + } + } + else if (Show_Border && borderingScan) { - col.a = alpha; - float4 original_color = image.Sample(textureSampler, v_in.uv); - col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; + return float4(1.0, 0.0, 0.0, 0.5); } - // result = float4(redness, greenness,blueness,1); - //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); - //color.rgb += col * alpha; - //color.a += clamp(1.0 - alpha, 0.0,1.0); - ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); - //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); - color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); + } + return float4(color, tex.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSZigZagShader { + +[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] +param( +# Set the radius of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the angle of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the period of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('period')] +[Single] +$Period, +# Set the amplitude of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the center_x of OBSZigZagShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSZigZagShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the phase of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('phase')] +[Single] +$Phase, +# Set the animate of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('animate')] +[Int32] +$Animate, +# Set the notes of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'ZigZag' +$ShaderNoun = 'OBSZigZagShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.0; +uniform float angle< + string label = "angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 180.0; +uniform float period< + string label = "period"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; +uniform float amplitude< + string label = "amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; + +uniform float center_x< + string label = "center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; +uniform float center_y< + string label = "center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; + +uniform float phase< + string label = "phase"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform int animate< + string label = "animate"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "No"; + int option_1_value = 1; + string option_1_label = "Amplitude"; + int option_2_value = 2; + string option_2_label = "Time"; +> = 0; + + +uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." + + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + + v_out.uv = 1. * v_in.uv - center; + + center.x /= ar; + v_out.uv.x /= ar; + + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float percent = (radius-dist)/radius; + float theta = percent * percent * + ( + animate == 1 ? + amplitude * sin(elapsed_time) : + amplitude + ) + * sin(percent * percent / period * radians(angle) + (phase + + ( + animate == 2 ? + elapsed_time : + 0 + ))); + + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; + + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } + +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSZoomBlurShader { + +[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] +param( +# Set the samples of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the magnitude of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('magnitude')] +[Single] +$Magnitude, +# Set the speed_percent of OBSZoomBlurShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the ease of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the glitch of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'zoom_blur' +$ShaderNoun = 'OBSZoomBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl +//for Media Player Classic HC or BE +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int samples < + string label = "Samples"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 32; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform int speed_percent < + string label = "Speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform bool glitch; +uniform string notes< + string widget_type = "info"; +> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; + +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; +} + +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float speed = speed_percent * 0.01; + + // circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); + + b = Styler(t, 0, c, d, ease); + float sample_speed = max(samples * b, 1.0); + + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample(textureSampler, v_in.uv); + + float xTrans = (v_in.uv.x*2)-1; + float yTrans = 1-(v_in.uv.y*2); + + float angle = atan(yTrans/xTrans) + PI; + if (sign(xTrans) == 1) { + angle+= PI; + } + float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); + + float2 currentCoord; + float4 accumulatedColor = float4(0,0,0,0); + + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor = currentColor; + + accumulatedColor = c0/sample_speed; + for(int i = 1; i< sample_speed; i++) { + float currentRadius ; + // Distance to center dependent + currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); + + // Continuous; + // currentRadius = max(0,radius - (0.0004 * i)); + + currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; + currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); + + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor += currentColor/sample_speed; + + } + + return accumulatedColor; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSZoomBlurTransitionShader { + +[Alias('Set-OBSZoomBlurTransitionShader','Add-OBSZoomBlurTransitionShader')] +param( +# Set the image_a of OBSZoomBlurTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSZoomBlurTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSZoomBlurTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSZoomBlurTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the strength of OBSZoomBlurTransitionShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'zoom_blur_transition' +$ShaderNoun = 'OBSZoomBlurTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/Ml3XR2 + +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; + +//modified zoom blur from http://transitions.glsl.io/transition/b86b90161503a0023231 +uniform float strength< + string label = "Strength (0.3)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.50; + float step = 0.01; +> = 0.3; +#define PI 3.141592653589793 + +float Linear_ease(in float begin, in float change, in float duration, in float time) { + return change * time / duration + begin; +} + +float Exponential_easeInOut(in float begin, in float change, in float duration, in float time) { + if (time == 0.0) + return begin; + else if (time == duration) + return begin + change; + time = time / (duration / 2.0); + if (time < 1.0) + return change / 2.0 * pow(2.0, 10.0 * (time - 1.0)) + begin; + return change / 2.0 * (-pow(2.0, -10.0 * (time - 1.0)) + 2.0) + begin; +} + +float Sinusoidal_easeInOut(in float begin, in float change, in float duration, in float time) { + return -change / 2.0 * (cos(PI * time / duration) - 1.0) + begin; +} + +float random(in float3 scale, in float seed) { + return frac(sin(dot(float3(seed, seed, seed), scale)) * 43758.5453 + seed); +} + +float3 crossFade(in float2 uv, in float dissolve) { + return lerp(image_a.Sample(textureSampler, uv).rgb, image_b.Sample(textureSampler, uv).rgb, dissolve); +} + +float4 mainImage(VertData v_in) : TARGET { + float2 texCoord = v_in.uv; + float progress = transition_time; + // Linear interpolate center across center half of the image + float2 center = float2(Linear_ease(0.5, 0.0, 1.0, progress),0.5); + float dissolve = Exponential_easeInOut(0.0, 1.0, 1.0, progress); + + // Mirrored sinusoidal loop. 0->strength then strength->0 + float strength2 = Sinusoidal_easeInOut(0.0, strength, 0.5, progress); + + float3 color = float3(0.0,0.0,0.0); + float total = 0.0; + float2 toCenter = center - texCoord; + + /* randomize the lookup values to hide the fixed float of samples */ + float offset = random(float3(12.9898, 78.233, 151.7182), 0.0)*0.5; + + for (float t = 0.0; t <= 20.0; t++) { + float percent = (t + offset) / 20.0; + float weight = 1.0 * (percent - percent * percent); + color += crossFade(texCoord + toCenter * percent * strength2, dissolve) * weight; + total += weight; + } + float4 rgba = float4(color / total, 1.0); + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSZoomShader { + +[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] +param( +# Set the center_x_percent of OBSZoomShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the power of OBSZoomShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'zoom' +$ShaderNoun = 'OBSZoomShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSZoomXYShader { + +[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] +param( +# Set the center_x_percent of OBSZoomXYShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomXYShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the x_power of OBSZoomXYShader +[Alias('x_power')] +[ComponentModel.DefaultBindingProperty('x_power')] +[Single] +$XPower, +# Set the y_power of OBSZoomXYShader +[Alias('y_power')] +[ComponentModel.DefaultBindingProperty('y_power')] +[Single] +$YPower, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'Zoom_XY' +$ShaderNoun = 'OBSZoomXYShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Zoom XY Shader + +// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ + +// The allow for an independent Horizontal and Vertical Zoom. + +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float x_power< + string label = "x power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +uniform float y_power< + string label = "y power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSAudioOutputSource { + + + #> + [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] + param( + # The name of the audio device. + # This name or device ID of the audio device that should be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ItemValue','ItemName','DeviceID')] + [string] + $AudioDevice, + + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + + # The name of the input. + # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, + + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' + + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + + } + begin { + # Audio Output sources have an inputKind of 'wasapi_output_capture'. + $inputKind = "wasapi_output_capture" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + + if (-not $myParameters["AudioDevice"]) { + $myParameters["AudioDevice"] = "default" + } + + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + + + if ($myParameters["AudioDevice"]) { + $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] + } + else { + $Name = $myParameters["Name"] = "AudioOutput" + } + } + + + + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + } + } + } + + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = $inputKind + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } + + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id + foreach ($deviceInfo in $possibleDevices) { + if ( + ($deviceInfo.itemName -eq $AudioDevice) -or + ($deviceInfo.ItemValue -eq $AudioDevice) -or + ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or + ($deviceInfo.ItemValue -like "*$AudioDevice*") -or + ($deviceInfo.ItemName -like "*$AudioDevice*") + ) { + $myParameterData["device_id"] = $deviceInfo.itemValue + break + } + } + + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return + } + + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Scene"] + } + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSBrowserSource { + + + [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] + param( + # The uri or file path to display. + # If the uri points to a local file, this will be preferred + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url', 'Href','Path','FilePath','FullName')] + [uri] + $Uri, + + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, + + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, + + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, + + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, + + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, + + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, + + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, + + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' + + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + $inputKind = "browser_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } + + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } + if ($uri.Scheme -eq 'File') { + if (Test-Path $uri.AbsolutePath) { + $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true + } + } + else + { + if (Test-Path $uri) { + $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) + $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true + } else { + $myParameterData["url"] = "$uri" + } + } + + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($uri.Segments) { + $uri.Segments[-1] + } elseif ($uri -match '[\\/]') { + @($uri -split '[\\/]')[-1] + } else { + $uri + } + } + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSColorSource { + + + [Alias('Add-OBSColorSource','Get-OBSColorSource')] + param( + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - } - return color; + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, - -} + [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + $script:AddOBSInput } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' + + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - continue nextParameter - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + begin { + $inputKind = "color_source_v3" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + $hexChar = [Regex]::new('[0-9a-f]') + $hexColors = @($hexChar.Matches($Color)) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + switch ($hexColors.Length) { + 8 { + #full rgba + $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') + } + 6 { + #rgb only, assume ff for alpha + $alpha = 0xff + $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + } + 4 { + #short rgba + $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') + } + 3 { + #short rgb, assume f for alpha + $alpha = 0xff + $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + } + 0 { + # No color provided, default to transparent black + $alpha = 0 + $red = 0 + $green = 0 + $blue = 0 } } + + $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + $realColor = [uint32]::Parse($hexColor,'HexNumber') + + + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "#$hexColor" } + + $myParameterData = [Ordered]@{color=$realColor} - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "color_source_v3" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } + + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } - } -} -} + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } -} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHalftoneShader { +function Set-OBSDisplaySource { + + + [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] + param( + # The monitor number. + # This the number of the monitor you would like to capture. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("monitor")] + [Alias('MonitorNumber','Display','DisplayNumber')] + [int] + $Monitor = 1, -[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] -param( -# Set the threshold of OBSHalftoneShader -[ComponentModel.DefaultBindingProperty('threshold')] -[Single] -$Threshold, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("capture_cursor")] + [switch] + $CaptureCursor, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, -process { -$shaderName = 'halftone' -$ShaderNoun = 'OBSHalftoneShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.1415926535897932384626433832795 -#define PI180 float(PI / 180.0) + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, -uniform float threshold< - string label = "Threshold"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.6; + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -float sind(float a) -{ - return sin(a * PI180); -} - -float cosd(float a) -{ - return cos(a * PI180); -} - -float added(float2 sh, float sa, float ca, float2 c, float d) -{ - return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); -} - -float4 mainImage(VertData v_in) : TARGET -{ - // Halftone dot matrix shader - // @author Tomek Augustyn 2010 - - // Ported from my old PixelBender experiment - // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk - - float coordX = v_in.uv.x; - float coordY = v_in.uv.y; - float2 dstCoord = float2(coordX, coordY); - float2 rotationCenter = float2(0.5, 0.5); - float2 shift = dstCoord - rotationCenter; - - float dotSize = 3.0; - float angle = 45.0; - - float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); - float4 srcPixel = image.Sample(textureSampler, dstCoord); - - float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; - float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); - - // uncomment to see how the raster pattern looks - // gray = rasterPattern; - - return float4(gray, gray, gray, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + $DynamicParameters + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - continue nextParameter - } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + } + } } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Users like 1 indexed, computers like zero-indexed. + $myParameterData["monitor"] = $Monitor - 1 + + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "Display $($Monitor)" } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "monitor_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name } + } } - -} - - -} - #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHardBlinkShader { - -[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] -param( -# Set the timeon of OBSHardBlinkShader -[ComponentModel.DefaultBindingProperty('timeon')] -[Single] -$Timeon, -# Set the timeoff of OBSHardBlinkShader -[ComponentModel.DefaultBindingProperty('timeoff')] -[Single] -$Timeoff, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +function Set-OBSMarkdownSource { + + + [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] + param( + # The markdown text, or the path to a markdown file + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Markdown, + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -process { -$shaderName = 'hard_blink' -$ShaderNoun = 'OBSHardBlinkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// hard_blink shader created by https://github.com/WhazzItToYa -// -// Periodically makes the source image 100% transparent, in configurable intervals. + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, -uniform float timeon< - string label = "Time On"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; + # The css style used to render the markdown. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", -uniform float timeoff< - string label = "Time Off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -float4 mainImage(VertData v_in) : TARGET -{ - float4 color = image.Sample(textureSampler, v_in.uv); - float m = timeon + timeoff; - float t = elapsed_time % m; - if (t < timeon) { - return color; - } else { - return float4(color.r, color.g, color.b, 0.0); - } -} + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + $script:AddOBSInput } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) } -} - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSHeatWaveSimpleShader { - -[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] -param( -# Set the Rate of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Rate')] -[Single] -$Rate, -# Set the Strength of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Single] -$Strength, -# Set the Distortion of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Distortion')] -[Single] -$Distortion, -# Set the Opacity of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Opacity')] -[Single] -$Opacity, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + $DynamicParameters + } + begin { + $inputKind = "markdown_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters -process { -$shaderName = 'heat-wave-simple' -$ShaderNoun = 'OBSHeatWaveSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Heat Wave Simple, Version 0.03, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// -// Description: -// Generate a crude pseudo heat wave displacement on an image source. -// -// Based on: https://www.shadertoy.com/view/td3GRn by Dombass -// -// Changelog: -// 0.03 - Added Opacity control -// 0.02 - Added crude Rate, Strength, and Distortion controls -// 0.01 - Initial release + $IsGet = $MyInvocation.InvocationName -like "Get-*" + $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' -uniform float Rate< - string label = "Rate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 5.0; -uniform float Strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = -25.0; - float maximum = 25.0; - float step = 0.01; -> = 1.0; -uniform float Distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 3.0; - float maximum = 20.0; - float step = 0.01; -> = 10.0; -uniform float Opacity< - string label = "Opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.00; + if ( + $IsGet -or + ($NoVerb -and -not $NonNameParameters) + ) { + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float distort = clamp(Distortion, 3.0, 20.0); + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - // Time varying pixel color - float jacked_time = Rate*elapsed_time; - float2 scale = float2(0.5, 0.5); - float str = clamp(Strength, -25.0, 25.0) * 0.01; - - uv += str * sin(scale*jacked_time + length( uv ) * distort); - float4 c = image.Sample( textureSampler, uv); - c.a *= saturate(Opacity*0.01); - return c; -} + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } + + $markdownAsUri = $null + if ($Markdown -like '*.md') { + $markdownAsUri = $markdown -as [uri] + if ($markdownAsUri.Scheme -eq 'File') { + $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["markdown_source"] = 1 } else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + $myParameterData["text"] = $Markdown + $myParameterData["markdown_source"] = 0 } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($markdownAsUri.Segments) { + $markdownAsUri.Segments[-1] + } elseif ($markdownAsUri -match '[\\/]') { + @($markdownAsUri -split '[\\/]')[-1] + } elseif ($markdownAsUri) { + $markdownAsUri + } else { + "Markdown" } - continue nextParameter - } - } + } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = "markdown_source" + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] } + } } - -} - - -} - #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHexagonShader { +function Set-OBSMediaSource { + + + [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file')] + [string] + $FilePath, -[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] -param( -# Set the Hex_Color of OBSHexagonShader -[Alias('Hex_Color')] -[ComponentModel.DefaultBindingProperty('Hex_Color')] -[String] -$HexColor, -# Set the Alpha_Percent of OBSHexagonShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Int32] -$AlphaPercent, -# Set the Quantity of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Quantity')] -[Single] -$Quantity, -# Set the Border_Width of OBSHexagonShader -[Alias('Border_Width')] -[ComponentModel.DefaultBindingProperty('Border_Width')] -[Int32] -$BorderWidth, -# Set the Blend of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Blend')] -[Management.Automation.SwitchParameter] -$Blend, -# Set the Equilateral of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Equilateral')] -[Management.Automation.SwitchParameter] -$Equilateral, -# Set the Zoom_Animate of OBSHexagonShader -[Alias('Zoom_Animate')] -[ComponentModel.DefaultBindingProperty('Zoom_Animate')] -[Management.Automation.SwitchParameter] -$ZoomAnimate, -# Set the Speed_Percent of OBSHexagonShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Int32] -$SpeedPercent, -# Set the Glitch of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Distort_X of OBSHexagonShader -[Alias('Distort_X')] -[ComponentModel.DefaultBindingProperty('Distort_X')] -[Single] -$DistortX, -# Set the Distort_Y of OBSHexagonShader -[Alias('Distort_Y')] -[ComponentModel.DefaultBindingProperty('Distort_Y')] -[Single] -$DistortY, -# Set the Offset_X of OBSHexagonShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSHexagonShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the notes of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # If set, the source will close when it is inactive. + # By default, this will be set to true. + # To explicitly set it to false, use -CloseWhenInactive:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("close_when_inactive")] + [switch] + $CloseWhenInactive, + # If set, the source will automatically restart. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("looping")] + [Alias('Looping')] + [switch] + $Loop, -process { -$shaderName = 'hexagon' -$ShaderNoun = 'OBSHexagonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 Hex_Color; -uniform int Alpha_Percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform float Quantity< - string label = "Quantity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 25; -uniform int Border_Width< - string label = "Border Width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 115; - int step = 1; -> = 15; // <- -15 to 85, -15 off top -uniform bool Blend; -uniform bool Equilateral; -uniform bool Zoom_Animate; -uniform int Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool Glitch; -uniform float Distort_X< - string label = "Distort X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Distort_Y< - string label = "Distort Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Offset_Y< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform string notes< - string widget_type = "info"; ->= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; + # If set, will use hardware decoding, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("hw_decode")] + [Alias('HardwareDecoding','hw_decode')] + [switch] + $UseHardwareDecoding, -float mod(float x, float y) -{ - return x - y * floor(x/y); -} + # If set, will clear the output on the end of the media. + # If this is set to false, the media will freeze on the last frame. + # This is set to true by default. + # To explicitly set to false, use -ClearMediaEnd:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("clear_on_media_end")] + [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] + [switch] + $ClearOnMediaEnd, -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x/y); -} + # Any FFMpeg demuxer options. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ffmpeg_options")] + [Alias('FFMpegOptions', 'FFMpeg_Options')] + [string] + $FFMpegOption, -// 0 on edges, 1 in non_edge -float hex(float2 p) { - float xyratio = 1; - if (Equilateral) - xyratio = uv_size.x /uv_size.y; + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, - // calc p - p.x = mul(p.x,xyratio); - p.y += mod(floor(p.x) , 2.0)*0.5; - p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); - return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); -} + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); - float alpha = float(Alpha_Percent) * 0.01; - float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); - float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; - float speed = float(Speed_Percent) * 0.01; - float time = (1 + sin(elapsed_time * speed))*0.5; - if (Zoom_Animate) - quantity *= time; + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, - // create a (pos)ition reference, hex radius and smoothstep out the non_edge - float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); - if (Glitch) - quantity *= lerp(pos.x, pos.y, rand_f); - float2 p = (pos * quantity); // number of hexes to be created - float r = (1.0 -0.7)*0.5; // cell default radius - float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - // make the border colorable - non_edge is scaled - float4 c = float4(non_edge, non_edge,non_edge,1.0) ; - if (non_edge < 1) - { - c = Hex_Color; - c.a = alpha; - if (Blend) - c = lerp(rgba, c, 1 - non_edge); - return lerp(rgba,c,alpha); - } - return lerp(rgba, c * rgba, alpha); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "ffmpeg_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + if ((Test-Path $FilePath)) { + $FilePathItem = Get-Item -Path $FilePath + $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' } - } -} -} + + if ($myParameters['InputSettings']) { + $keys = + @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { + $myParameters['InputSettings'].Keys + } else { + foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { + $prop.Name + } + }) -} + foreach ($key in $keys) { + $myParameterData[$key] = $myParameters['InputSettings'].$key + } + $myParameterData.remove('inputSettings') + } -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSHslHsvSaturationShader { - -[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] -param( -# Set the hslSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] -[Single] -$HslSaturationFactor, -# Set the hslGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslGamma')] -[Single] -$HslGamma, -# Set the hsvSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] -[Single] -$HsvSaturationFactor, -# Set the hsvGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvGamma')] -[Single] -$HsvGamma, -# Set the adjustmentOrder of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('adjustmentOrder')] -[Int32] -$AdjustmentOrder, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - - -process { -$shaderName = 'hsl_hsv_saturation' -$ShaderNoun = 'OBSHslHsvSaturationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' - -// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions - -uniform float hslSaturationFactor< - string label = "HSL Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; + if (-not $Name) { + + $Name = $myParameters["Name"] = + if ($FilePathItem.Name) { + $FilePathItem.Name + } else { + "Media" + } + + } -uniform float hslGamma< - string label = "HSL Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } -uniform float hsvSaturationFactor< - string label = "HSV Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } -uniform float hsvGamma< - string label = "HSV Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } -uniform int adjustmentOrder< - string label = "Order"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; - int option_1_value = 2; - string option_1_label = "HSL first, then HSV"; - int option_2_value = 3; - string option_2_label = "HSV first, then HSL"; -> = 1; + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 -// HSV conversion + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } -float3 rgb2hsv(float3 c) { - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + + # Otherwise, if we had a result + if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen + } - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -float3 hsv2rgb(float3 c) { - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSoundCloudSource { + + + [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] + param( + # The uri to display. This must point to a SoundCloud URL. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url','SoundCloudUri','SoundCloudUrl')] + [uri] + $Uri, -// HSL conversion - -float3 rgb2hsl(float3 c) { - float maxVal = max(c.r, max(c.g, c.b)); - float minVal = min(c.r, min(c.g, c.b)); - float delta = maxVal - minVal; - float h = 0.0; - float s = 0.0; - float l = (maxVal + minVal) / 2.0; - - if(delta != 0) { - if(l < 0.5) s = delta / (maxVal + minVal); - else s = delta / (2.0 - maxVal - minVal); + # If set, will not autoplay. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoAutoPlay, - if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); - else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; - else h = (c.r - c.g) / delta + 4.0; + # If set, will not display album artwork. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoArtwork, - h /= 6.0; - } + # If set, will not display play count. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoPlayCount, - return float3(h, s, l); -} + # If set, will not display uploader info. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoUploaderInfo, -float hue2rgb(float p, float q, float t) { - if(t < 0.0) t += 1.0; - if(t > 1.0) t -= 1.0; - if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; - if(t < 1.0/2.0) return q; - if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; - return p; -} + # If provided, will start playing at a given track number. + [Parameter(ValueFromPipelineByPropertyName)] + [int] + $TrackNumber, -float3 hsl2rgb(float3 c) { - float r, g, b; + # If set, will show a share link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowShare, - if(c.y == 0.0) { - r = g = b = c.z; - } else { - float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; - float p = 2.0 * c.z - q; - r = hue2rgb(p, q, c.x + 1.0/3.0); - g = hue2rgb(p, q, c.x); - b = hue2rgb(p, q, c.x - 1.0/3.0); - } + # If set, will show a download link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowDownload, - return float3(r, g, b); -} + # If set, will show a buy link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowBuy, -float3 adjustColorWithOrder(float3 originalColor) { - if (adjustmentOrder == 1) { - // Parallel adjustment (both HSL and HSV operate on the original image and then blend) - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 hslAdjustedColor = hsl2rgb(hslAdjusted); - - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); - - float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; - return finalColor; - } - else if (adjustmentOrder == 2) { - // HSL first, then HSV - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 afterHSL = hsl2rgb(hslAdjusted); - float3 hsvAdjusted = rgb2hsv(afterHSL); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - return hsv2rgb(hsvAdjusted); - } - else if (adjustmentOrder == 3) { - // HSV first, then HSL - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 afterHSV = hsv2rgb(hsvAdjusted); - float3 hslAdjusted = rgb2hsl(afterHSV); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - return hsl2rgb(hslAdjusted); - } - return originalColor; // Default to original color in case of unexpected values -} + # The color used for the SoundCloud audio bars and buttons. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, -// Final composite + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -float4 mainImage(VertData v_in) : TARGET -{ - float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; - float3 adjustedColor = adjustColorWithOrder(originalColor); - return float4(adjustedColor, 1.0); // preserving the original alpha -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + # The name of the input. + # If no name is provided, then "SoundCloud" will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, + + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput } else { - Set-OBSShaderFilter @ShaderFilterSplat + $script:AddOBSInput } - } -} + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -} + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + # Sound Cloud Sources are really Browser Sources. + $inputKind = "browser_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSHueRotatonShader { -[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] -param( -# Set the Speed of OBSHueRotatonShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Hue_Override of OBSHueRotatonShader -[Alias('Hue_Override')] -[ComponentModel.DefaultBindingProperty('Hue_Override')] -[Management.Automation.SwitchParameter] -$HueOverride, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { + Write-Error "URI must be from SoundCloud.com" + return + } + if ($uri.Query) { + $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') + } -process { -$shaderName = 'hue-rotaton' -$ShaderNoun = 'OBSHueRotatonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hue Rotation shader, version 1.0 for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Description: -// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue -// override option is provided to force a specific rotating hue instead of the original image''s hue. -// -// Changelog: -// 1.0 - Initial release + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } | + Where-Object { + $_.Settings['LocalFile'] -like '*.SoundCloud.*' + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as published by the Free Software Foundation. + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 10.00; -uniform bool Hue_Override = false; + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return saturate(float3(R,G,B)); -} + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } -#define Epsilon 1e-10 - -float3 RGBtoHCV(in float3 RGB) -{ - // Based on work by Sam Hocevar and Emil Persson - float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); - float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); - float C = Q.x - min(Q.w, Q.y); - float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); - return float3(H, C, Q.x); -} + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } -float3 HSVtoRGB(in float3 HSV) -{ - float3 RGB = HUEtoRGB(HSV.x); - return ((RGB - 1) * HSV.y + 1) * HSV.z; -} + $MyObsPowerShellPath = if ($home) { + Join-Path $home ".obs-powershell" + } -float3 RGBtoHSV(in float3 RGB) -{ - float3 HCV = RGBtoHCV(RGB); - float S = HCV.y / (HCV.z + Epsilon); - return float3(HCV.x, S, HCV.z); -} + $ThisSoundCloudSourceFileName = + if ($name) { + "${name}.SoundCloudSource.html" + } else { + "SoundCloudSource.html" + } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 col_in = image.Sample(textureSampler, uv); - float3 col_out; - float3 HSV = RGBtoHSV(col_in.rgb); + $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName + - if(Hue_Override) - HSV.x = elapsed_time * Speed * 0.01; - else - HSV.x += elapsed_time * Speed * 0.01; + $soundCloudSrc = @( + "https://w.soundcloud.com/player/?url=" + $Uri + "&" + @( + if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} + if ($color) { "color=$color" -replace "\#",'%23'} + if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} + if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } + if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} + if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } + if ($ShowDownload) { "download=true"} else { "download=false" } + if ($ShowBuy) { "buying=true"} else { "buying=false" } + if ($ShowShare) { "sharing=true"} else { "sharing=false" } + ) -join '&' + ) -join '' + + $soundCloudWidget = @( + "" + "" + "" + "" + "" + ) -join ' ' - // Normalize Hue - HSV.x = frac(HSV.x); - - col_out = HSVtoRGB(HSV); - return float4(col_out, col_in.a); -} + $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force + + $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + if (-not $Name) { + $Name = $myParameters['Name'] = 'SoundCloud' } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } } - } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSwitchSource { + + + [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Sources')] + [string[]] + $SourceList, - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." } -} + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, -} + # If set, the list of sources will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, + # If set, will switch between sources. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch")] + [switch] + $TimeSwitch, -} + # The interval between sources + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_between")] + [timespan] + $Interval, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSIntensityScopeShader { + # The duration between sources that are switching at a time. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_duration")] + [timespan] + $Duration, -[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] -param( -# Set the gain of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('gain')] -[Single] -$Gain, -# Set the blend of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('blend')] -[Single] -$Blend, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # The item that will be switched in a TimeSwitch, after -Duration and -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $TimeSwitchTo = "Next", + # If set, will switch on the underlying source's media state events. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("media_state_switch")] + [switch] + $MediaStateSwitch, -process { -$shaderName = 'intensity-scope' -$ShaderNoun = 'OBSIntensityScopeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Robin Green, Dec 2016 -// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. -// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro -uniform float gain< - string label = "Gain"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 1.00; - float step = 0.01; -> = 0.3; -uniform float blend< - string label = "Blend"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.6; + # The change in media state that should trigger a switch + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] + $MediaStateChange, -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.y = 1.0 - uv.y; - - // calculate the intensity bucket for this pixel based on column height (padded at the top) - const float max_value = 270.0; - const float buckets = 512.0; - float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); - float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); - - // count the count the r,g,b and luma in this column that match the bucket - float4 count = float4(0.0, 0.0, 0.0, 0.0); - for( int i=0; i < 512; ++i ) { - float j = float(i) / buckets; - float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; - - // calculate the Rec.709 luma for this pixel - pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; + # When the source switcher is trigger by media end, this determines the next source that will be switched to. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $MediaSwitchTo = "Next", - float4 logpixel = log(pixel); - if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; - if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; - if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; - if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; - } - - // sum luma into RGB, tweak log intensity for readability - count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; - - // output - return count; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + # The name of the transition between sources. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $TransitionName, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # The properties sent to the transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $TransitionProperty, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # The name of the transition used to show a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $ShowTransition, - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + # The properties sent to the show transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $ShowTransitionProperty, - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + # The transition used to hide a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + } + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") } else { - Set-OBSShaderFilter @ShaderFilterSplat + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } - } -} - -} + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $HideTransition, + # The properties sent to the hide transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $HideTransitionProperty, -} + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInvertLumaShader { + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] -param( -# Set the Invert_Color of OBSInvertLumaShader -[Alias('Invert_Color')] -[ComponentModel.DefaultBindingProperty('Invert_Color')] -[Management.Automation.SwitchParameter] -$InvertColor, -# Set the Invert_Luma of OBSInvertLumaShader -[Alias('Invert_Luma')] -[ComponentModel.DefaultBindingProperty('Invert_Luma')] -[Management.Automation.SwitchParameter] -$InvertLuma, -# Set the Gamma_Correction of OBSInvertLumaShader -[Alias('Gamma_Correction')] -[ComponentModel.DefaultBindingProperty('Gamma_Correction')] -[Management.Automation.SwitchParameter] -$GammaCorrection, -# Set the Test_Ramp of OBSInvertLumaShader -[Alias('Test_Ramp')] -[ComponentModel.DefaultBindingProperty('Test_Ramp')] -[Management.Automation.SwitchParameter] -$TestRamp, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -process { -$shaderName = 'invert-luma' -$ShaderNoun = 'OBSInvertLumaShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Invert shader 1.0 - for OBS Shaderfilter -// Copyright 2021 by SkeletonBow -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv -// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -uniform bool Invert_Color = false; -uniform bool Invert_Luma = true; -uniform bool Gamma_Correction = true; -uniform bool Test_Ramp = false; + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "source_switcher" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' -float3 encodeSRGB(float3 linearRGB) -{ - float3 a = float3(12.92,12.92,12.92) * linearRGB; - float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); - float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); - return float3(lerp(a, b, c)); -} + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -float3 decodeSRGB(float3 screenRGB) -{ - float3 a = screenRGB / float3(12.92,12.92,12.92); - float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); - float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); - return float3(lerp(a, b, c)); -} + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); -} + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + if ($myParameters[$parameter.Name] -is [timespan]) { + $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds + } + } + } + + + $selectedIndex = -1 + $sourcesObject = @( + $currentIndex = -1 + foreach ($sourceName in $SourceList) { + $currentIndex++ + $selected = ($null -ne $Select) -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [string] -and + ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) + ) + ) + if ($selected) { + $selectedIndex = $currentIndex + } + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} + } + ) -float3 RGBtoYUV(float3 color) -{ - // YUV matriz (BT709 luma coefficients) -#ifdef OPENGL - float3x3 toYUV = float3x3( - float3(0.2126, -0.09991, 0.615), - float3(0.7152, -0.33609, -0.55861), - float3(0.0722, 0.436, -0.05639)); -#else - float3x3 toYUV = { - { 0.2126, -0.09991, 0.615 }, - { 0.7152, -0.33609, -0.55861 }, - { 0.0722, 0.436, -0.05639 }, - }; -#endif - return mul(color, toYUV); -} + if ($sourcesObject) { + $myParameterData['sources'] = $sourcesObject + if ($selectedIndex -gt 0) { + $myParameterData["current_index"] = $selectedIndex + } + } + elseif ($Select -is [int]) { + $myParameterData['current_index'] = $Select + } + -float3 YUVtoRGB(float3 color) -{ - // YUV matriz (BT709) -#ifdef OPENGL - float3x3 fromYUV = float3x3( - float3(1.000, 1.000, 1.000), - float3(0.0, -0.21482, 2.12798), - float3(1.28033, -0.38059, 0.0)); -#else - float3x3 fromYUV = { - { 1.000, 1.000, 1.000 }, - { 0.0, -0.21482, 2.12798 }, - { 1.28033, -0.38059, 0.0 }, - }; -#endif - return mul(color, fromYUV); -} + + if ($TransitionName) { + if ($TransitionName -notlike '*_transition') { + $TransitionName = "${TransitionName}_transition" + } + $myParameterData["transition"] = $TransitionName + } + + if ($TransitionProperty) { + $myParameterData["transition_properties"] = $TransitionProperty + } -float3 generate_ramps(float3 color, float2 uv) -{ - float3 ramp = float3(0.0, 0.0, 0.0); - if(uv.y < 0.2) - ramp.r = uv.x; // Red ramp - else if(uv.y < 0.4) - ramp.g = uv.x; // Green ramp - else if(uv.y < 0.6) - ramp.b = uv.x; // Blue ramp - else if(uv.y < 0.8) - ramp = float3(uv.x, uv.x, uv.x); // Grey ramp - else - ramp = HUEtoRGB(uv.x); // Hue rainbow - - return ramp; -} + if ($ShowTransition) { + if ($ShowTransition -notlike '*_transition') { + $ShowTransition = "${ShowTransition}_transition" + } + $myParameterData["show_transition"] = $ShowTransition + } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 obstex = image.Sample( textureSampler, uv ); - float3 color = obstex.rgb; - // Apply sRGB gamma transfer encode - if(Gamma_Correction) color = encodeSRGB( color ); - // Override display with test patterns to visually see what is happening - if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); - // RGB color invert - if( Invert_Color ) { - color = float3(1.0, 1.0, 1.0) - color; - } - // YUV luma invert - if( Invert_Luma ) { - float3 yuv = RGBtoYUV( color ); - yuv.x = 1.0 - yuv.x; - color = YUVtoRGB(yuv); - } - // Apply sRGB gamma transfer decode - if(Gamma_Correction) color = decodeSRGB( color ); - return float4(color, obstex.a); -} + if ($ShowTransitionProperty) { + $myParameterData["show_transition_properties"] = $ShowTransitionProperty + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + if ($HideTransition) { + if ($HideTransition -notlike '*_transition') { + $HideTransition = "${HideTransition}_transition" } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + $myParameterData["hide_transition"] = $ShowTransition } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + if ($HideTransitionProperty) { + $myParameterData["hide_transition_properties"] = $HideTransitionProperty + } + + if ($TimeSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["time_switch_to"] = $vvi + break } - continue nextParameter } } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($MediaSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_state_switch_to"] = $vvi + break + } + } } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + if ($MediaStateChange) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_switch_state"] = $vvi + break + } } } + + if (-not $Name) { + $Name = $myParameters["Name"] = "Source Switcher" + } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + if ($sceneItem) { + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSVLCSource { + + + [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file','Playlist')] + [string[]] + $FilePath, -} + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + # If an `[IO.FileInfo]` is provided, this will be the exact file. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." + } + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, + # If set, will shuffle the playlist + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shuffle")] + [switch] + $Shuffle, -} + # If set, the playlist will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminance2Shader { + # If set, will show subtitles, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle_enable")] + [Alias('ShowSubtitles','Subtitles')] + [switch] + $Subtitle, -[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] -param( -# Set the color of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the lumaMax of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMax')] -[Single] -$LumaMax, -# Set the lumaMin of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMaxSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] -[Single] -$LumaMaxSmooth, -# Set the lumaMinSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the invertImageColor of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # The selected audio track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("track")] + [int] + $AudioTrack, + # The selected subtitle track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle")] + [int] + $SubtitleTrack, -process { -$shaderName = 'luminance2' -$ShaderNoun = 'OBSLuminance2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 color; -uniform float lumaMax< - string label = "Luma Max"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.05; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMaxSmooth< - string label = "Luma Max Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.10; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; -uniform string notes< - string widget_type = "info"; -> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -float4 mainImage(VertData v_in) : TARGET -{ + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - //intensity = min(max(intensity,minIntensity),maxIntensity); - float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); - float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); - float amask = clo * chi; + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if (invertAlphaChannel) - { - amask = 1.0 - amask; } - rgba *= color; - rgba.a = clamp(amask, 0.0, 1.0); + begin { + filter OutputAndFitToScreen { + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "vlc_source" + } - return rgba; -} + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # (either way, if we were called Get- or with no verb, we're done now). + return } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - continue nextParameter - } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } + + $allPaths = @(foreach ($path in $FilePath) { + foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { + $_.Path + } + }) + $playlistObject = @( + $currentIndex = 0 + foreach ($path in $allPaths) { + $currentIndex++ + $selected = $Select -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or + ($select -is [string] -and + ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) + ) + ) + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} + } + ) + $myParameterData['playlist'] = $playlistObject + + if (-not $Name) { + $Name = $myParameters["Name"] = $FilePathItem.Name } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSWaveformSource { + + + [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] + param( + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + # The audio source for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("audio_source")] + [string] + $AudioSource, -} + # The display mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("display_mode")] + [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] + [string] + $DisplayMode, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceAlphaShader { + # The render mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("line","solid","gradient")] + [string] + $RenderMode, -[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] -param( -# Set the ViewProj of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSLuminanceAlphaShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSLuminanceAlphaShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSLuminanceAlphaShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSLuminanceAlphaShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSLuminanceAlphaShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSLuminanceAlphaShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the color_matrix of OBSLuminanceAlphaShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the mul_val of OBSLuminanceAlphaShader -[Alias('mul_val')] -[ComponentModel.DefaultBindingProperty('mul_val')] -[Single] -$MulVal, -# Set the add_val of OBSLuminanceAlphaShader -[Alias('add_val')] -[ComponentModel.DefaultBindingProperty('add_val')] -[Single] -$AddVal, -# Set the level of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('level')] -[Single] -$Level, -# Set the invertAlphaChannel of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + # The windowing mode for the waveform. + # This is the mathematical function used to determine the current "window" of audio data. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("hann","hamming","blackman","blackman_harris","none")] + [string] + $WindowMode, + + # The color used for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_base")] + [PSObject] + $Color, + + # The crest color used for the waveform. + # This will be ignored if the render mode is not "gradient". + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_crest")] + [PSObject] + $CrestColor, + + # The channel mode for the waveform. + # This can be either mono or stereo. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_mode")] + [ValidateSet("mono","stereo")] + [string] + $ChannelMode, + + # The number of pixels between each channel in stereo mode + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_spacing")] + [int] + $ChannelSpacing, + + # If set, will use a radial layout for the waveform + # Radial layouts will ignore the desired height of the source and instead create a square. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("radial_layout")] + [switch] + $RadialLayout, + + # If set, will invert the direction for a radial waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("invert_direction")] + [switch] + $InvertRadialDirection, + + # If set, will normalize the volume displayed in the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("normalize_volume")] + [switch] + $NoramlizeVolume, + + # If set, will automatically declare an FFTSize + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("auto_fft_size")] + [switch] + $AutoFftSize, + + # If set, will attempt to make audio peaks render faster. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fast_peaks")] + [switch] + $FastPeak, + + # The width of the waveform bar. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_width")] + [int] + $BarWidth, + + # The gap between waveform bars. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_gap")] + [int] + $BarGap, + # The width of waveform bar step. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_width")] + [int] + $StepWidth, -process { -$shaderName = 'luminance_alpha' -$ShaderNoun = 'OBSLuminanceAlphaShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; + # The gap between waveform bar steps. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_gap")] + [int] + $StepGap, -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; + # The low-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_low")] + [int] + $LowCutoff, -uniform float4x4 color_matrix; -uniform float4 color; -uniform float mul_val< - string label = "Mulitply"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float add_val< - string label = "Add"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 0.0; -uniform float level< - string label = "Level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> =1.0; -uniform bool invertAlphaChannel; + # The high-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_high")] + [int] + $HighCutoff, -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # The floor of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("floor")] + [int] + $Floor, -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # The ceiling of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ceiling")] + [int] + $Ceiling, -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("slope")] + [double] + $Slope, -VertDataOut mainTransform(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); - vert_out.uv = v_in.uv * mul_val + add_val; - vert_out.uv2 = v_in.uv ; - return vert_out; -} + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_q")] + [Alias('RollOffOctaves')] + [double] + $RollOffOctave, -/*float3 GetLuminance(float4 rgba) -{ - float red = rbga.r; - float green = rgba.g; - float blue = rgba.b; - return (.299 * red) + (.587 * green) + (.114 * blue); -}*/ + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_rate")] + [double] + $RollOffRate, -float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv) ; - if (rgba.a > 0.0) - { - - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; - } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - - } - return rgba; -} + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("grad_ratio")] + [double] + $GradientRatio, -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = PSAlphaMaskRGBA(v_in); - } -} + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("deadzone")] + [double] + $Deadzone, + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("temporal_smoothing")] + [ValidateSet("none","exp_moving_avg")] + [string] + $TemporalSmoothing, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, + + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + $script:AddOBSInput } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' + + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - continue nextParameter - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + begin { + $inputKind = "phandasm_waveform_source" + filter ToOBSColor { + + if ($_ -is [uint32]) { $_ } + elseif ($_ -is [string]) { + if ($_ -match '^\#[a-f0-9]{3,4}$') { + $_ = $_ -replace '[a-f0-9]','$0$0' + } + + if ($_ -match '^#[a-f0-9]{8}$') { + ( + '0x' + + (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') + ) -as [UInt32] + } + elseif ($_ -match '^#[a-f0-9]{6}$') { + + ( + '0xff' + + (($_ -replace '#').ToCharArray()[-1..-6] -join '') + ) -as [UInt32] + } + } + } + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + $MyInvocationName = "$($MyInvocation.InvocationName)" + $myVerb, $myNoun = $MyInvocationName -split '-' + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + $IsGet = $myVerb -eq "Get" + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + if ( + $IsGet -or + $NoVerb + ) { + $inputsOfKind = + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + if ($NonNameParameters -and -not $IsGet) { + $paramCopy = [Ordered]@{} + $PSBoundParameters + if ($paramCopy.Name) { $paramCopy.Remove('Name') } + $inputsOfKind | & $myScriptBlock @paramCopy + } else { + $inputsOfKind + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat + + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($AudioSource) { + "$($AudioSource)-Waveform" + } else { + "Waveform" + } + } + + if ($myParameterData.color_base) { + $myParameterData.color_base = $myParameterData.color_base | ToOBSColor } - } -} -} + if ($myParameterData.color_crest) { + $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor + } + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 -} + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceShader { - -[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] -param( -# Set the color of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the level of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('level')] -[Single] -$Level, -# Set the invertImageColor of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +function Set-OBSWindowSource { + + + [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] + param( + # The monitor number. + # This the number of the monitor you would like to capture. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] + [string] + $WindowTitle, + # The number of the capture method. By default, automatic (0). + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("method")] + [int] + $CaptureMethod, -process { -$shaderName = 'Luminance' -$ShaderNoun = 'OBSLuminanceShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 22, 2022 -uniform float4 color; -uniform float level< - string label = "Level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; + # The capture priority. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet('ExactMatch','SameType','SameExecutable')] + [string] + $CapturePriority, -uniform string notes< - string widget_type = "info"; -> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cursor")] + [switch] + $CaptureCursor, -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} + # If set, will capture the client area. + # This will be set by default. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("client_area")] + [switch] + $ClientArea, -float4 mainImage(VertData v_in) : TARGET -{ + # If set, will force SDR. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("force_sdr")] + [switch] + $ForceSDR, - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - //intensity = min(max(intensity,minIntensity),maxIntensity); + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, + + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - + $DynamicParameters + } - return rgba; -} + begin { + $InputKind = "window_capture" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + + if (-not $myParameters["WindowTitle"]) { + while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { + $_ = $_.Parent + } + if ($_.MainWindowTitle) { + $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" + } + } + + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + if ($myParameters["WindowTitle"]) { + $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] } else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + $Name = "WindowCapture" } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + + + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - continue nextParameter - } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($null -ne $CaptureMethod) { + $myParameterData["method"] = $CaptureMethod + } + if ($CapturePriority -eq 'ExactMatch') { + $myParameterData["priority"] = 1 + } + elseif ($CapturePriority -eq 'SameType') { + $myParameterData["priority"] = 0 + } + elseif ($CapturePriority -eq 'SameExecutable') { + $myParameterData["priority"] = 2 } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "window_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window + foreach ($windowInfo in $possibleWindows) { + if (-not $WindowTitle) { continue } + if ( + ($windowInfo.itemName -eq $WindowTitle) -or + ($windowInfo.ItemValue -eq $WindowTitle) -or + ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or + ($windowInfo.ItemValue -like "*$WindowTitle*") -or + ($windowInfo.ItemName -like "*$WindowTitle*") + ) { + $myParameterData["window"] = $windowInfo.itemValue + break + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name + } } else { - Set-OBSShaderFilter @ShaderFilterSplat + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. } + } } + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSInput { -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] +[Alias('obs.powershell.websocket.CreateInput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSMatrixShader { +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, -[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] -param( -# Set the ViewProj of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSMatrixShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSMatrixShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSMatrixShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSMatrixShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSMatrixShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSMatrixShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSMatrixShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSMatrixShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSMatrixShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the mouse of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('mouse')] -[Single[]] -$Mouse, -# Set the Invert_Direction of OBSMatrixShader -[Alias('Invert_Direction')] -[ComponentModel.DefaultBindingProperty('Invert_Direction')] -[Management.Automation.SwitchParameter] -$InvertDirection, -# Set the lumaMin of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Ratio of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('Ratio')] -[Single] -$Ratio, -# Set the Alpha_Percentage of OBSMatrixShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSMatrixShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'matrix' -$ShaderNoun = 'OBSMatrixShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float -uniform float4x4 ViewProj; -uniform texture2d image; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float2 mouse< - string label = "Virtual Mouse Coordinates"; - string widget_type = "slider"; - float2 minimum = {0, 0}; - float2 maximum = {100., 100.}; - float2 scale = {.01, .01}; - float2 step = {.01, .01}; -> = {0., 0.}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -int2 iMouse() { - return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); } -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; -/* ps start +} -*/ + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSProfile { -uniform bool Invert_Direction< - string label = "Invert Direction"; -> = true; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float Ratio< - string label = "Ratio"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 4.0; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100; // -uniform bool Apply_To_Alpha_Layer = true; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] +[Alias('obs.powershell.websocket.CreateProfile')] +param( -#define PI2 6.28318530718 -#define PI 3.1416 +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float vorocloud(float2 p){ - float f = 0.0; - float flow = 1.0; - float time = elapsed_time; - if(Invert_Direction){ - flow *= -1; - } - /* - //periodically stop - if (loops % 16 >= 8.0) - { - time = local_time - elapsed_time; - } - */ - - float r = clamp(Ratio,-50,50); - float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); - p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); - - float2 pts[4]; - - pts[0] = float2(0.5, 0.6); - pts[1] = float2(-0.4, 0.4); - pts[2] = float2(0.2, -0.7); - pts[3] = float2(-0.3, -0.4); - - float d = 5.0; - - for(int i = 0; i < 4; i++){ - pts[i].x += 0.03 * cos(float(i)) + p.x; - pts[i].y += 0.03 * sin(float(i)) + p.y; - d = min(d, distance(pts[i], pp)); - } - - f = 2.0 * pow(1.0 - 0.3 * d, 13.0); - - f = min(f, 1.0); - - return f; -} +process { -vec4 scene(float2 UV){ - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - float x = UV.x; - float y = UV.y; - - float2 p = float2(x, y) - 0.5; - - vec4 col = vec4(0.0,0.0,0.0,0.0); - col.g += 0.02; - - float v = vorocloud(p); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.6 * v; - col.b += 0.5 * pow(v, 5.0); - - - v = vorocloud(p * 2.0); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.2 * v; - col.b += 0.01 * pow(v, 5.0); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - col.a = 1.0; - float luma = dot(col.rgb,float3(0.299,0.587,0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min,0.0,1.0); + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float4 original_color = image.Sample(textureSampler, UV); - - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - if (Apply_To_Alpha_Layer == false) - original_color.a = alpha; - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } - else - { - col.a = original_color.a; - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return col; } -void mainImage( out vec4 fragColor, in vec2 fragCoord ) -{ - vec2 uv = fragCoord.xy / uv_size; - fragColor = scene(uv); -} -/*ps end*/ +} -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSScene { -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] +[Alias('obs.powershell.websocket.CreateScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSSceneCollection { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] +[Alias('obs.powershell.websocket.CreateSceneCollection')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -43026,155 +50052,127 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMotionBlurShader { +function Add-OBSSceneItem { -[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] +[Alias('obs.powershell.websocket.CreateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Add-OBSSceneSource')] param( -# Set the previous_output of OBSMotionBlurShader -[Alias('previous_output')] -[ComponentModel.DefaultBindingProperty('previous_output')] -[String] -$PreviousOutput, -# Set the strength of OBSMotionBlurShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'motion_blur' -$ShaderNoun = 'OBSMotionBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d previous_output; -uniform float strength< - string label = "strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -float4 mainImage(VertData v_in) : TARGET -{ - return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -43183,147 +50181,125 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMultiplyShader { +function Add-OBSSourceFilter { -[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] +[Alias('obs.powershell.websocket.CreateSourceFilter')] param( -# Set the other_image of OBSMultiplyShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] -[String] -$OtherImage, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'multiply' -$ShaderNoun = 'OBSMultiplyShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return base * other; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -43332,511 +50308,436 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSNightSkyShader { +function Copy-OBSSceneItem { -[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] +[Alias('obs.powershell.websocket.DuplicateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the speed of OBSNightSkyShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the Include_Clouds of OBSNightSkyShader -[Alias('Include_Clouds')] -[ComponentModel.DefaultBindingProperty('Include_Clouds')] -[Management.Automation.SwitchParameter] -$IncludeClouds, -# Set the Include_Moon of OBSNightSkyShader -[Alias('Include_Moon')] -[ComponentModel.DefaultBindingProperty('Include_Moon')] -[Management.Automation.SwitchParameter] -$IncludeMoon, -# Set the center_width_percentage of OBSNightSkyShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSNightSkyShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, -# Set the Alpha_Percentage of OBSNightSkyShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSNightSkyShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSNightSkyShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the number_stars of OBSNightSkyShader -[Alias('number_stars')] -[ComponentModel.DefaultBindingProperty('number_stars')] -[Int32] -$NumberStars, -# Set the SKY_COLOR of OBSNightSkyShader -[Alias('SKY_COLOR')] -[ComponentModel.DefaultBindingProperty('SKY_COLOR')] -[String] -$SKYCOLOR, -# Set the STAR_COLOR of OBSNightSkyShader -[Alias('STAR_COLOR')] -[ComponentModel.DefaultBindingProperty('STAR_COLOR')] -[String] -$STARCOLOR, -# Set the LIGHT_SKY of OBSNightSkyShader -[Alias('LIGHT_SKY')] -[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] -[String] -$LIGHTSKY, -# Set the SKY_LIGHTNESS of OBSNightSkyShader -[Alias('SKY_LIGHTNESS')] -[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] -[Single] -$SKYLIGHTNESS, -# Set the MOON_COLOR of OBSNightSkyShader -[Alias('MOON_COLOR')] -[ComponentModel.DefaultBindingProperty('MOON_COLOR')] -[String] -$MOONCOLOR, -# Set the moon_size of OBSNightSkyShader -[Alias('moon_size')] -[ComponentModel.DefaultBindingProperty('moon_size')] -[Single] -$MoonSize, -# Set the moon_bump_size of OBSNightSkyShader -[Alias('moon_bump_size')] -[ComponentModel.DefaultBindingProperty('moon_bump_size')] -[Single] -$MoonBumpSize, -# Set the Moon_Position_x of OBSNightSkyShader -[Alias('Moon_Position_x')] -[ComponentModel.DefaultBindingProperty('Moon_Position_x')] -[Single] -$MoonPositionX, -# Set the Moon_Position_y of OBSNightSkyShader -[Alias('Moon_Position_y')] -[ComponentModel.DefaultBindingProperty('Moon_Position_y')] -[Single] -$MoonPositionY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneName')] +[string] +$DestinationSceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] +[string] +$DestinationSceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'night_sky' -$ShaderNoun = 'OBSNightSkyShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 -// https://github.com/Oncorporation/obs-shaderfilter -//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 20.0; -uniform bool Include_Clouds = true; -uniform bool Include_Moon = true; -uniform int center_width_percentage< - string label = "Center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "Center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 95.0; // -uniform bool Apply_To_Image = false; -uniform bool Replace_Image_Color = false; -uniform int number_stars< - string label = "Number stars"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; // -uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; -uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; -uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; -uniform float SKY_LIGHTNESS< - string label = "SKY LIGHTNESS"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = .3; - // Moon -uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; -uniform float moon_size< - string label = "Moon size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.18; -uniform float moon_bump_size< - string label = "Moon bump size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.14; -uniform float Moon_Position_x< - string label = "Moon Position x"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.6; -uniform float Moon_Position_y< - string label = "Moon Position y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.3; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#define PI 3.1416 + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU -float noise11(float p) { - return frac(sin(p*633.1847) * 9827.95); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" -float noise21(float2 p) { - return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float2 noise22(float2 p) { - return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise -float cosineInterpolation(float a, float b, float x) { - float ft = x * PI; - float f = (1. - cos(ft)) * .5; - return a * (1. - f) + b * f; } -float smoothNoise11(float p, float dist) { - float prev = noise11(p-dist); - float next = noise11(p+dist); - - return cosineInterpolation(prev, next, .5); -} -float smoothNoise21(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - //smoothstep function: maybe change it later! - lv = lv*lv*(3.-2.*lv); - - float bl = noise21(id); - float br = noise21(id+float2(1.,0.)); - float b = lerp(bl, br, lv.x); - - float tl = noise21(id+float2(0.,1.)); - float tr = noise21(id+float2(1.,1.)); - float t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} +} -float2 smoothNoise22(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - lv = lv*lv*(3.-2.*lv); - - float2 bl = noise22(id); - float2 br = noise22(id+float2(1.,0.)); - float2 b = lerp(bl, br, lv.x); - - float2 tl = noise22(id+float2(0.,1.)); - float2 tr = noise22(id+float2(1.,1.)); - float2 t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentPreviewScene { -float valueNoise11(float p) { - float c = smoothNoise11(p, 0.5); - c += smoothNoise11(p, 0.25)*.5; - c += smoothNoise11(p, 0.125)*.25; - c += smoothNoise11(p, 0.0625)*.125; - - return c /= .875; -} -float valueNoise21(float2 uv) { - float c = smoothNoise21(uv, 4.); - c += smoothNoise21(uv, 8.)*.5; - c += smoothNoise21(uv, 16.)*.25; - c += smoothNoise21(uv, 32.)*.125; - c += smoothNoise21(uv, 64.)*.0625; - - return c /= .9375; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float2 valueNoise22(float2 uv) { - float2 c = smoothNoise22(uv, 4.); - c += smoothNoise22(uv, 8.)*.5; - c += smoothNoise22(uv, 16.)*.25; - c += smoothNoise22(uv, 32.)*.125; - c += smoothNoise22(uv, 64.)*.0625; + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - return c /= .9375; -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 points(float2 p, float2 uv, float3 color, float size, float blur) { - float dist = distance(p, uv); - return color*smoothstep(size, size*(0.999-blur), dist); } -float mapInterval(float x, float a, float b, float c, float d) { - return (x-a)/(b-a) * (d-c) + c; -} -float blink(float time, float timeInterval) { - float halfInterval = timeInterval / 2.0; - //Get relative position in the bucket - float p = fmod(time, timeInterval); - - - if (p <= timeInterval / 2.) { - return smoothstep(0., 1., p/halfInterval); - } else { - return smoothstep(1., 0., (p-halfInterval)/halfInterval); - } -} +} -float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { - float dist = distance(p, uv); - float3 BumpSamples = float3(0.,0.,0.); - - if (dist < radius) { - float bumps = (1.-valueNoise21(uv*spin))*.1; - BumpSamples = float3(bumps, bumps, bumps); - } - return BumpSamples; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentProgramScene { -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba;// = image.Sample(textureSampler, v_in.uv); - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st; - // Normalized pixel coordinates (from 0 to 1) - float2 uv = v_in.uv; - float2 ouv = uv; - uv -= .5; - uv.x *= uv_size.x/uv_size.y; - - float2 seed = toCenter / uv_size.xy; - - float time = elapsed_time + seed.x*speed; - - //float3 col = float3(0.0); - //float m = valueNoise21(uv); - float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); - - col *= SKY_LIGHTNESS - (1.-ouv.y); - - //Add clouds - if (Include_Clouds) - { - float2 timeUv = uv; - timeUv.x += time*.1; - timeUv.y += valueNoise11(timeUv.x+.352)*.01; - float cloud = valueNoise21(timeUv); - col += cloud*.1; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] +[Alias('obs.powershell.websocket.GetCurrentProgramScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - //Add stars in the top part of the scene - float timeInterval = speed *.5; //5.0 - float timeBucket = floor(time / timeInterval); - float2 moonPosition = float2(-1000, -1000); - if (Include_Moon) - { - moonPosition = float2(Moon_Position_x, Moon_Position_y); - col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); - // Moon bumps - col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); - } +process { - for (float i = 0.; i < clamp(number_stars,0,100); i++) { - float2 starPosition = float2(i/10., i/10.); - - starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); - starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); - - float starIntensity = blink(time+ (rand_f * i), timeInterval ); - //Hide stars that are behind the moon - if (distance(starPosition, moonPosition) > moon_size) { - col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; - col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; - } - } - //col = float3(blink(time, timeInterval)); - rgba = float4(col,alpha); - // Output to screen - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,alpha); - - } - return rgba; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentSceneTransition { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -43845,155 +50746,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOpacityShader { +function Get-OBSCurrentSceneTransitionCursor { -[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Opacity of OBSOpacityShader -[ComponentModel.DefaultBindingProperty('Opacity')] -[Single] -$Opacity, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'opacity' -$ShaderNoun = 'OBSOpacityShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Opacity shader - for OBS Shaderfilter -// Copyright 2021 by SkeltonBowTV -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv -uniform float Opacity< - string label = "Opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.01; -> = 100.00; // 0.00 - 100.00 percent -float4 mainImage( VertData v_in ) : TARGET -{ - float4 color = image.Sample( textureSampler, v_in.uv ); - return float4( color.rgb, color.a * Opacity * 0.01 ); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -44002,217 +50849,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelShader { +function Get-OBSGroup { -[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] +[Alias('obs.powershell.websocket.GetGroupList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Speed of OBSPagePeelShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Position of OBSPagePeelShader -[ComponentModel.DefaultBindingProperty('Position')] -[Single] -$Position, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'page-peel' -$ShaderNoun = 'OBSPagePeelShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Page Peel, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Based on Shadertoy shader by droozle -// -// Description: -// -// -// Changelog: -// 0.01 - Initial release - -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 50.0; - float step = 0.001; -> = 1.00; -uniform float Position< - string label = "Position"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 0.0; -float4 mainImage( VertData v_in ) : TARGET -{ - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; - float t = Position + elapsed_time * Speed; - // Define the fold. - float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - // Sample texture. - float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - col *= 0.25; // Background color (peeled off). - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - float4 back = image.Sample( textureSampler, pt ); // Back color. - back.rgb = back.rgb * 0.25 + 0.75; - - float shadow = smoothstep( 0.0, 0.2, -side ); - back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); - - // Support for transparency. - col = lerp( col, back.rgb, back.a ); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - - // Output to screen - return float4(col,1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -44221,230 +50952,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelTransitionShader { +function Get-OBSGroupSceneItem { -[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] +[Alias('obs.powershell.websocket.GetGroupSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the image_a of OBSPagePeelTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSPagePeelTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSPagePeelTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPagePeelTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the page_color of OBSPagePeelTransitionShader -[Alias('page_color')] -[ComponentModel.DefaultBindingProperty('page_color')] -[String] -$PageColor, -# Set the page_transparency of OBSPagePeelTransitionShader -[Alias('page_transparency')] -[ComponentModel.DefaultBindingProperty('page_transparency')] -[Single] -$PageTransparency, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'page-peel-transition' -$ShaderNoun = 'OBSPagePeelTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; -uniform float page_transparency< - string label = "Page Transparency"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -float4 mainImage(VertData v_in) : TARGET -{ - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; - - float t = transition_time * 12.0 + 11.0; - // Define the fold. - float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - - // Sample texture. - float3 col = float3(1.0,0.0,0.0); - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - // Next page - col = image_b.Sample( textureSampler, uv ).rgb; - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - col = image_a.Sample( textureSampler, pt ).rgb; // Back color. - col = lerp(page_color.rgb, col, page_transparency); - - float shadow = smoothstep( 0.0, 0.2, -side ); - col = lerp( col * 0.2, col, shadow ); - }else{ - col = image_a.Sample( textureSampler, uv ).rgb; - } - } - - // Output to screen - if(convert_linear) - col = srgb_nonlinear_to_linear(col); - return float4(col,1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -44453,429 +51065,209 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPerlinNoiseShader { +function Get-OBSHotkey { -[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] +[Alias('obs.powershell.websocket.GetHotkeyList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the speed of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the animated of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the apply_to_channel of OBSPerlinNoiseShader -[Alias('apply_to_channel')] -[ComponentModel.DefaultBindingProperty('apply_to_channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the inverted of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('inverted')] -[Management.Automation.SwitchParameter] -$Inverted, -# Set the multiply of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('multiply')] -[Management.Automation.SwitchParameter] -$Multiply, -# Set the speed_horizonal of OBSPerlinNoiseShader -[Alias('speed_horizonal')] -[ComponentModel.DefaultBindingProperty('speed_horizonal')] -[Single] -$SpeedHorizonal, -# Set the speed_vertical of OBSPerlinNoiseShader -[Alias('speed_vertical')] -[ComponentModel.DefaultBindingProperty('speed_vertical')] -[Single] -$SpeedVertical, -# Set the iterations of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('iterations')] -[Int32] -$Iterations, -# Set the white_noise of OBSPerlinNoiseShader -[Alias('white_noise')] -[ComponentModel.DefaultBindingProperty('white_noise')] -[Single] -$WhiteNoise, -# Set the black_noise of OBSPerlinNoiseShader -[Alias('black_noise')] -[ComponentModel.DefaultBindingProperty('black_noise')] -[Single] -$BlackNoise, -# Set the notes of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'perlin_noise' -$ShaderNoun = 'OBSPerlinNoiseShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// -// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -// -// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson -// Translation and modification was made by Keijiro Takahashi. -// Conversion for OBS by Charles Fettinger. -// -// This shader is based on the webgl-noise GLSL shader. For further details -// of the original shader, please see the following description from the -// original source code. -// - // -// GLSL textureless classic 2D noise "cnoise", (white_noise) -// with an RSL-style periodic variant "pnoise" (black_noise). -// Author: Stefan Gustavson (stefan.gustavson@liu.se) -// Version: 2011-08-22 -// -// Many thanks to Ian McEwan of Ashima Arts for the -// ideas for permutation and gradient selection. -// -// Copyright (c) 2011 Stefan Gustavson. All rights reserved. -// Distributed under the MIT license. See LICENSE file. -// https://github.com/ashima/webgl-noise -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} - float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} - float4 permute(float4 x) -{ - return mod289(((x*34.0)+1.0)*x); -} - float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} - float2 fade(float2 t) { - return t*t*t*(t*(t*6.0-15.0)+10.0); -} - // Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - // Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - //The good bits~ adapting the noise generator for the plugin and giving some control over the shader - //todo: pseudorandom number generator w/ seed -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform bool animated; -uniform bool apply_to_channel; -uniform bool inverted; -uniform bool multiply; -uniform float speed_horizonal< - string label = "Speed horizontal"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform float speed_vertical< - string label = "Speed vertical"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0; -uniform int iterations< - string label = "Iterations"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 4; -//how much c_noise do we want? white -uniform float white_noise< - string label = "White noise"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -//how much p_noise do we want? black -uniform float black_noise< - string label = "Black noise"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform string notes< - string widget_type = "info"; -> = "white noise and black noise and iterations.. enjoy!"; - float2 noisePosition(float t){ - return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); -} - float4 mainImage(VertData v_in) : TARGET -{ - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - float2 dir = float2(speed_horizonal,speed_vertical); - - if(!animated){ - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = v_in.uv * scale; - float2 period = float2(scale * 2.0, scale * 2.0); - - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } - - //o += pnoise(coord, period) * w; - - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); - } - } else { - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = (v_in.uv + t*dir) * scale; - float2 period = float2(scale * 2.0, scale * 2.0); - - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } - - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); - } - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] +[Alias('obs.powershell.websocket.GetInputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -44884,256 +51276,337 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPerspectiveShader { +function Get-OBSInputAudioBalance { -[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] +[Alias('obs.powershell.websocket.GetInputAudioBalance')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the angle_x of OBSPerspectiveShader -[Alias('angle_x')] -[ComponentModel.DefaultBindingProperty('angle_x')] -[Single] -$AngleX, -# Set the angle_y of OBSPerspectiveShader -[Alias('angle_y')] -[ComponentModel.DefaultBindingProperty('angle_y')] -[Single] -$AngleY, -# Set the angle_z of OBSPerspectiveShader -[Alias('angle_z')] -[ComponentModel.DefaultBindingProperty('angle_z')] -[Single] -$AngleZ, -# Set the perspective of OBSPerspectiveShader -[ComponentModel.DefaultBindingProperty('perspective')] -[Single] -$Perspective, -# Set the border_color of OBSPerspectiveShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the show_border of OBSPerspectiveShader -[Alias('show_border')] -[ComponentModel.DefaultBindingProperty('show_border')] -[Management.Automation.SwitchParameter] -$ShowBorder, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'perspective' -$ShaderNoun = 'OBSPerspectiveShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Perspective Transform Shader for OBS -// Allows adjustable 3D perspective effects -// Usage: Add as filter in OBS via ShaderFilter plugin -uniform float angle_x< - string label = "X Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; -uniform float angle_y< - string label = "Y Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float angle_z< - string label = "Z Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float perspective< - string label = "Perspective Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float4 border_color< - string label = "Border Color"; -> = {0.0, 0.0, 0.0, 1.0}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform bool show_border< - string label = "Show Border"; -> = true; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4x4 rotationMatrix(float3 angles) -{ - float radX = radians(angles.x); - float radY = radians(angles.y); - float radZ = radians(angles.z); - - float sinX = sin(radX); - float cosX = cos(radX); - float sinY = sin(radY); - float cosY = cos(radY); - float sinZ = sin(radZ); - float cosZ = cos(radZ); - - return float4x4( - cosY*cosZ, -cosY*sinZ, sinY, 0, - sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, - -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, - 0, 0, 0, 1 - ); } -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - - // Center coordinates - float2 center = float2(0.5, 0.5); - uv -= center; - - // Apply perspective - float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); - uv *= perspectiveFactor; - - // Create rotation matrix - float3 angles = float3(angle_x, angle_y, angle_z); - float4x4 rotMat = rotationMatrix(angles); - - // Apply transformation - float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); - - // Restore center position - uv = transformed.xy + center; - - // Sample texture with border handling - if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { - return show_border ? border_color : float4(0, 0, 0, 0); - } - - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioMonitorType { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioSyncOffset { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -45142,402 +51615,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPieChartShader { +function Get-OBSInputAudioTracks { -[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] +[Alias('obs.powershell.websocket.GetInputAudioTracks')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the inner_radius of OBSPieChartShader -[Alias('inner_radius')] -[ComponentModel.DefaultBindingProperty('inner_radius')] -[Single] -$InnerRadius, -# Set the outer_radius of OBSPieChartShader -[Alias('outer_radius')] -[ComponentModel.DefaultBindingProperty('outer_radius')] -[Single] -$OuterRadius, -# Set the start_angle of OBSPieChartShader -[Alias('start_angle')] -[ComponentModel.DefaultBindingProperty('start_angle')] -[Single] -$StartAngle, -# Set the total of OBSPieChartShader -[ComponentModel.DefaultBindingProperty('total')] -[Int32] -$Total, -# Set the part_1 of OBSPieChartShader -[Alias('part_1')] -[ComponentModel.DefaultBindingProperty('part_1')] -[Int32] -$Part1, -# Set the color_1 of OBSPieChartShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the part_2 of OBSPieChartShader -[Alias('part_2')] -[ComponentModel.DefaultBindingProperty('part_2')] -[Int32] -$Part2, -# Set the color_2 of OBSPieChartShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the part_3 of OBSPieChartShader -[Alias('part_3')] -[ComponentModel.DefaultBindingProperty('part_3')] -[Int32] -$Part3, -# Set the color_3 of OBSPieChartShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the part_4 of OBSPieChartShader -[Alias('part_4')] -[ComponentModel.DefaultBindingProperty('part_4')] -[Int32] -$Part4, -# Set the color_4 of OBSPieChartShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] -[String] -$Color4, -# Set the part_5 of OBSPieChartShader -[Alias('part_5')] -[ComponentModel.DefaultBindingProperty('part_5')] -[Int32] -$Part5, -# Set the color_5 of OBSPieChartShader -[Alias('color_5')] -[ComponentModel.DefaultBindingProperty('color_5')] -[String] -$Color5, -# Set the part_6 of OBSPieChartShader -[Alias('part_6')] -[ComponentModel.DefaultBindingProperty('part_6')] -[Int32] -$Part6, -# Set the color_6 of OBSPieChartShader -[Alias('color_6')] -[ComponentModel.DefaultBindingProperty('color_6')] -[String] -$Color6, -# Set the part_7 of OBSPieChartShader -[Alias('part_7')] -[ComponentModel.DefaultBindingProperty('part_7')] -[Int32] -$Part7, -# Set the color_7 of OBSPieChartShader -[Alias('color_7')] -[ComponentModel.DefaultBindingProperty('color_7')] -[String] -$Color7, -# Set the part_8 of OBSPieChartShader -[Alias('part_8')] -[ComponentModel.DefaultBindingProperty('part_8')] -[Int32] -$Part8, -# Set the color_8 of OBSPieChartShader -[Alias('color_8')] -[ComponentModel.DefaultBindingProperty('color_8')] -[String] -$Color8, -# Set the part_9 of OBSPieChartShader -[Alias('part_9')] -[ComponentModel.DefaultBindingProperty('part_9')] -[Int32] -$Part9, -# Set the color_9 of OBSPieChartShader -[Alias('color_9')] -[ComponentModel.DefaultBindingProperty('color_9')] -[String] -$Color9, -# Set the part_10 of OBSPieChartShader -[Alias('part_10')] -[ComponentModel.DefaultBindingProperty('part_10')] -[Int32] -$Part10, -# Set the color_10 of OBSPieChartShader -[Alias('color_10')] -[ComponentModel.DefaultBindingProperty('color_10')] -[String] -$Color10, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'pie-chart' -$ShaderNoun = 'OBSPieChartShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float inner_radius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 32.0; -uniform float outer_radius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 50.0; -uniform float start_angle< - string label = "Start angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 90.0; -uniform int total< - string label = "Total"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 100; -uniform int part_1< - string label = "Part 1"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 50; -uniform float4 color_1 = {0.0,0.26,0.62,1.0}; -uniform int part_2< - string label = "Part 2"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 25; -uniform float4 color_2 = {0.24,0.40,0.68,1.0}; -uniform int part_3< - string label = "Part 3"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 10; -uniform float4 color_3 = {0.38,0.56,0.75,1.0}; -uniform int part_4< - string label = "Part 4"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 5; -uniform float4 color_4 = {0.52,0.72,0.81,1.0}; -uniform int part_5< - string label = "Part 5"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 3; -uniform float4 color_5 = {0.69,0.87,0.86,1.0}; -uniform int part_6< - string label = "Part 6"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 2; -uniform float4 color_6 = {1.0,0.79,0.73,1.0}; -uniform int part_7< - string label = "Part 7"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_7 = {0.99,0.57,0.57,1.0}; -uniform int part_8< - string label = "Part 8"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_8 = {0.91,0.36,0.44,1.0}; -uniform int part_9< - string label = "Part 9"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_9 = {0.77,0.16,0.32,1.0}; -uniform int part_10< - string label = "Part 10"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 0; -uniform float4 color_10 = {0.58,0.0,0.23,1.0}; -float4 mainImage(VertData v_in) : TARGET -{ - const float pi = 3.14159265358979323846; -#ifdef OPENGL - float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); - float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); -#else - float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; - float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; -#endif - float2 center = float2(0.5, 0.5); - float2 factor; - if(uv_size.x < uv_size.y){ - factor = float2(1.0, uv_size.y/uv_size.x); - }else{ - factor = float2(uv_size.x/uv_size.y, 1.0); - } - center = center * factor; - float d = distance(center, v_in.uv * factor); - if(d > outer_radius/100.0 || d < inner_radius/100.0){ - return image.Sample(textureSampler, v_in.uv); - } - float2 toCenter = center - v_in.uv*factor; - float angle = atan2(toCenter.y ,toCenter.x); - angle = angle - (start_angle / 180.0 * pi); - if(angle < 0.0) - angle = pi + pi + angle; - if(angle < 0.0) - angle = pi + pi + angle; - angle = angle / (pi + pi); - float t = 0.0; - for(int i = 0; i < 10; i+=1) { - float part = parts[i]/total; - if(angle > t && angle <= t+part){ - return colors[i]; - } - t = t + part; - } - return image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -45546,192 +51728,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationShader { +function Get-OBSInputDefaultSettings { -[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] +[Alias('obs.powershell.websocket.GetInputDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Target_Width of OBSPixelationShader -[Alias('Target_Width')] -[ComponentModel.DefaultBindingProperty('Target_Width')] -[Single] -$TargetWidth, -# Set the Target_Height of OBSPixelationShader -[Alias('Target_Height')] -[ComponentModel.DefaultBindingProperty('Target_Height')] -[Single] -$TargetHeight, -# Set the notes of OBSPixelationShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'pixelation' -$ShaderNoun = 'OBSPixelationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// with help from SkeltonBowTV -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform float Target_Width< - string label = "Target Width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 320.0; -uniform float Target_Height< - string label = "Target Height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 180.0; -uniform string notes< - string widget_type = "info"; -> = "adjust width and height to your screen dimension"; - -float4 mainImage(VertData v_in) : TARGET -{ - float targetWidth = Target_Width; - if(targetWidth < 2.0) - targetWidth = 2.0; - float targetHeight = Target_Height; - if(targetHeight < 2.0) - targetHeight = 2.0; - float2 tex1; - int pixelSizeX = int(uv_size.x / targetWidth); - int pixelSizeY = int(uv_size.y / targetHeight); - - int pixelX = int(v_in.uv.x * uv_size.x); - int pixelY = int(v_in.uv.y * uv_size.y); - - tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; - tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; - - float4 c1 = image.Sample(textureSampler, tex1 ); - return c1; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -45740,210 +51836,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationTransitionShader { +function Get-OBSInputKind { -[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] +[Alias('obs.powershell.websocket.GetInputKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the transition_time of OBSPixelationTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPixelationTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the power of OBSPixelationTransitionShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the center_x of OBSPixelationTransitionShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSPixelationTransitionShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('unversioned')] +[switch] +$Unversioned, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'pixelation-transition' -$ShaderNoun = 'OBSPixelationTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = 0.5; - float maximum = 8.0; - float step = 0.01; -> = 3.0; -uniform float center_x< - string label = "X"; - string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Y"; - string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -float4 mainImage(VertData v_in) : TARGET -{ - //1..0..1 - float scale = abs(transition_time - 0.5) * 2.0; - scale = pow(scale, power); - float2 uv = v_in.uv; - uv -= float2(center_x, center_y); - uv *= uv_size; - uv *= scale; - uv = floor(uv); - uv /= scale; - uv /= uv_size; - uv += float2(center_x, center_y); - uv = clamp(uv, 1.0/uv_size, 1.0); - float4 rgba = image.Sample(textureSampler, uv); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -45952,238 +51944,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPolarShader { +function Get-OBSInputMute { -[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] +[Alias('obs.powershell.websocket.GetInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the center_x of OBSPolarShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSPolarShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the point_y of OBSPolarShader -[Alias('point_y')] -[ComponentModel.DefaultBindingProperty('point_y')] -[Single] -$PointY, -# Set the flip of OBSPolarShader -[ComponentModel.DefaultBindingProperty('flip')] -[Management.Automation.SwitchParameter] -$Flip, -# Set the rotate of OBSPolarShader -[ComponentModel.DefaultBindingProperty('rotate')] -[Single] -$Rotate, -# Set the repeat of OBSPolarShader -[ComponentModel.DefaultBindingProperty('repeat')] -[Single] -$Repeat, -# Set the scale of OBSPolarShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'polar' -$ShaderNoun = 'OBSPolarShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 -#define PI_2 6.2831 -#define mod(x,y) (x - y * floor(x / y)) - -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -uniform float point_y< - string label = "Point y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform bool flip; -uniform float rotate< - string label = "Rotate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float repeat< - string label = "Repeat"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 1.0; - -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.x -= center_x ; - uv.y -= center_y ; - uv.x = uv.x * ( uv_size.x / uv_size.y); - float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; - if(repeat < 1.0){ - pixel_angle = mod(pixel_angle+rotate,1.0); - if(pixel_angle > repeat) - return float4(0,0,0,0); - pixel_angle = mod(pixel_angle/repeat,1.0); - } else { - pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); - } - float pixel_distance = length(uv)/ scale - point_y; - float2 uv2 = float2(pixel_angle , pixel_distance); - if(flip) - uv2 = float2(1.0,1.0) - uv2; - return image.Sample(textureSampler,uv2); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -46192,259 +52057,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPulseShader { - -[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] -param( -# Set the ViewProj of OBSPulseShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSPulseShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSPulseShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSPulseShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSPulseShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSPulseShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSPulseShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSPulseShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the speed of OBSPulseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSPulseShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSPulseShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] -[Single] -$MaxGrowthPixels, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - - -process { -$shaderName = 'pulse' -$ShaderNoun = 'OBSPulseShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; +function Get-OBSInputPropertiesListPropertyItems { -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 0.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 200.0; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] +[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, -VertData mainTransform(VertData v_in) -{ - VertData vert_out; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - float3 pos = v_in.pos.xyz; - float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); - float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; - float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float t = (1 + sin(elapsed_time * speed)) / 2; - float3 current_pos = min_pos * (1 - t) + max_pos * t; - vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} +process { -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -46453,331 +52175,224 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRainbowShader { +function Get-OBSInputSettings { -[Alias('Set-OBSRainbowShader','Add-OBSRainbowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] +[Alias('obs.powershell.websocket.GetInputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Saturation of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Saturation')] -[Single] -$Saturation, -# Set the Luminosity of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Luminosity')] -[Single] -$Luminosity, -# Set the Spread of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Spread')] -[Single] -$Spread, -# Set the Speed of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Alpha_Percentage of OBSRainbowShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Vertical of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the Rotational of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Rotational')] -[Management.Automation.SwitchParameter] -$Rotational, -# Set the Rotation_Offset of OBSRainbowShader -[Alias('Rotation_Offset')] -[ComponentModel.DefaultBindingProperty('Rotation_Offset')] -[Single] -$RotationOffset, -# Set the Apply_To_Image of OBSRainbowShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSRainbowShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSRainbowShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSRainbowShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Notes of OBSRainbowShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rainbow' -$ShaderNoun = 'OBSRainbowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Rainbow shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 13, 2022 -uniform float Saturation< - string label = "Saturation"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.8; // -uniform float Luminosity< - string label = "Luminosity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; // -uniform float Spread< - string label = "Spread"; - string widget_type = "slider"; - float minimum = 0.5; - float maximum = 10.0; - float step = 0.01; -> = 3.8; // -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 2.4; // -uniform float Alpha_Percentage< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; // -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.001; -> = 0.0; // -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string Notes< - string widget_type = "info"; -> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; - -float hueToRGB(float v1, float v2, float vH) { - vH = frac(vH); - if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); - if ((2.0 * vH) < 1.0) return (v2); - if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); - return clamp(v1, 0.0, 1.0); -} - -float4 HSLtoRGB(float4 hsl) { - float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); - float v1 = 0.0; - float v2 = 0.0; - - if (hsl.y == 0) { - rgb.xyz = hsl.zzz; - } - else { - - if (hsl.z < 0.5) { - v2 = hsl.z * (1 + hsl.y); - } - else { - v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); - } - - v1 = 2.0 * hsl.z - v2; - - rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); - rgb.y = hueToRGB(v1, v2, hsl.x); - rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); - - } - - return rgb; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - - //set colors and direction - float hue = (-1 * lPos.x) / 2.0; - if (Rotational && (Vertical == false)) - { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - hue = (lPos.x * cosine + lPos.y * sine) * 0.5; - } - if (Vertical && (Rotational == false)) - { - hue = (-1 * lPos.y) * 0.5; - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - hue += time; - hue = frac(hue); - float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); - float4 rgba = HSLtoRGB(hsl); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; - float4 luma_color = float4(luma, luma, luma, luma); - if (Replace_Image_Color) - color = luma_color; - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } - return rgba; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputVolume { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] +[Alias('obs.powershell.websocket.GetInputVolume')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -46786,396 +52401,420 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRainWindowShader { +function Get-OBSLastReplayBufferReplay { -[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] +[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the size of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('size')] -[Single] -$Size, -# Set the blurSize of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('blurSize')] -[Single] -$BlurSize, -# Set the trail_strength of OBSRainWindowShader -[Alias('trail_strength')] -[ComponentModel.DefaultBindingProperty('trail_strength')] -[Single] -$TrailStrength, -# Set the trail_color of OBSRainWindowShader -[Alias('trail_color')] -[ComponentModel.DefaultBindingProperty('trail_color')] -[Single] -$TrailColor, -# Set the speed of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the debug of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('debug')] -[Management.Automation.SwitchParameter] -$DebugShader, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rain-window' -$ShaderNoun = 'OBSRainWindowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro -// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 -// https://www.shadertoy.com/view/ltffzl -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. -uniform float size< - string label = "Rain Drop Size"; - string widget_type = "slider"; - float minimum = 0.001; - float maximum = 0.5; - float step = 0.01; -> = 0.2; -uniform float blurSize< - string label = "Blur Radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 32.0; // BLUR SIZE (Radius) -uniform float trail_strength< - string label = "Trail Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform float trail_color< - string label = "Trail Color"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 40.0; -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.01; -> = 100.0; -uniform bool debug = false; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float fract(float v){ - return v - floor(v); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 fract3(float3 v){ - return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 fract4(float4 v){ - return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); } -float3 N13(float p) { - // from DAVE HOSKINS - float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); - p3 += dot(p3, p3.yzx + 19.19); - return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); -} +} -float4 N14(float t) { - return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); -} -float N(float t) { - return fract(sin(t*12345.564)*7658.76); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSMediaInputStatus { -float Saw(float b, float t) { - return smoothstep(0., b, t)*smoothstep(1., b, t); -} -float2 Drops(float2 uv, float t) { - - float2 UV = uv; - - // DEFINE GRID - uv.y += t*0.8; - float2 a = float2(6., 1.); - float2 grid = a*2.; - float2 id = floor(uv*grid); - - // RANDOM SHIFT Y - float colShift = N(id.x); - uv.y += colShift; - - // DEFINE SPACES - id = floor(uv*grid); - float3 n = N13(id.x*35.2+id.y*2376.1); - float2 st = fract2(uv*grid)-float2(.5, 0); - - // POSITION DROPS - //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 - float x = n.x-.5; - - float y = UV.y*20.; - - float distort = sin(y+sin(y)); - x += distort*(.5-abs(x))*(n.z-.5); - x *= .7; - float ti = fract(t+n.z); - y = (Saw(.85, ti)-.5)*.9+.5; - float2 p = float2(x, y); - - // DROPS - float d = length((st-p)*a.yx); - - float dSize = size; - - float Drop = smoothstep(dSize, .0, d); - - - float r = sqrt(smoothstep(1., y, st.y)); - float cd = abs(st.x-x); - - // TRAILS - float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); - float trailFront = smoothstep(-.02, .02, st.y-y); - trail *= trailFront; - - - // DROPLETS - y = UV.y; - y += N(id.x); - float trail2 = smoothstep(dSize*r, .0, cd); - float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; - y = fract(y*10.)+(st.y-.5); - float dd = length(st-float2(x, y)); - droplets = smoothstep(dSize*N(id.x), 0., dd); - float m = Drop+droplets*r*trailFront; - if(debug){ - m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES - } - +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] +[Alias('obs.powershell.websocket.GetMediaInputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - return float2(m, trail); -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float StaticDrops(float2 uv, float t) { - uv *= 30.; - - float2 id = floor(uv); - uv = fract2(uv)-.5; - float3 n = N13(id.x*107.45+id.y*3543.654); - float2 p = (n.xy-.5)*0.5; - float d = length(uv-p); - - float fade = Saw(.025, fract(t+n.z)); - float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return c; } -float2 Rain(float2 uv, float t) { - //float s = StaticDrops(uv, t); - float2 r1 = Drops(uv, t); - float2 r2 = Drops(uv*1.8, t); - float c; - if(debug){ - c = r1.x; - }else{ - c = r1.x+r2.x;//s+r1.x+r2.x; - } - - c = smoothstep(.3, 1., c); - - if(debug){ - return float2(c, r1.y); - }else{ - return float2(c, max(r1.y, r2.y)); - } -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; - uv.y = 1.0 - uv.y; - uv = uv * uv_scale; - float2 UV = v_in.uv; - float T = elapsed_time * speed / 100.0; - - float t = T*.2; - - UV = (UV-.5)*(.9)+.5; +} - float2 c = Rain(uv, t); + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSMonitor { - float2 e = float2(.001, 0.); //pixel offset - float cx = Rain(uv+e, t).x; - float cy = Rain(uv+e.yx, t).x; - float2 n = float2(cx-c.x, cy-c.x); //normals - // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj - float Pi = 6.28318530718; // Pi*2 +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] +[Alias('obs.powershell.websocket.GetMonitorList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // GAUSSIAN BLUR SETTINGS {{{ - float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) - // GAUSSIAN BLUR SETTINGS }}} - float2 Radius = blurSize/uv_size; - float3 col = image.Sample(textureSampler, UV).rgb; - if(blurSize > 0.0){ - // Blur calculations - for(float d=0.0; dAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSOutput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] +[Alias('obs.powershell.websocket.GetOutputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -47184,199 +52823,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRectangularDropShadowShader { +function Get-OBSOutputSettings { -[Alias('Set-OBSRectangularDropShadowShader','Add-OBSRectangularDropShadowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] +[Alias('obs.powershell.websocket.GetOutputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the shadow_offset_x of OBSRectangularDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] -[Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSRectangularDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] -[Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSRectangularDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] -[Int32] -$ShadowBlurSize, -# Set the shadow_color of OBSRectangularDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] -[String] -$ShadowColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rectangular_drop_shadow' -$ShaderNoun = 'OBSRectangularDropShadowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 22, 2022 -uniform int shadow_offset_x< - string label = "shadow offset x"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; ->; -uniform int shadow_offset_y< - string label = "shadow offset y"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 1; - -uniform float4 shadow_color; -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); - float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); - float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); - - float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); - float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); - float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); - - return final_shadow_color * (1-color.a) + color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -47385,204 +52931,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSReflectShader { +function Get-OBSOutputStatus { -[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] +[Alias('obs.powershell.websocket.GetOutputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Horizontal of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the center_x_percent of OBSReflectShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSReflectShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Reflect' -$ShaderNoun = 'OBSReflectShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Reflect Shader - -// Reflects horizontally and/or vertically. - -uniform bool Horizontal< - string label = "Reflect horizontally"; -> = false; -uniform bool Vertical< - string label = "Reflect vertically"; -> = true; - -uniform int center_x_percent< - string label = "center x percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - - if (Horizontal == true) { - if (pos.x < center_pos.x) { - pos.x = center_pos.x - pos.x; - } else if (pos.x == center_pos.x) { - pos.x = pos.x; - } else { - pos.x = pos.x - center_pos.x; - } - } - if (Vertical == true) { - if (pos.y < center_pos.y) { - pos.y = center_pos.y - pos.y; - } else if (pos.y == center_pos.y) { - pos.y = pos.y; - } else { - pos.y = pos.y - center_pos.y; - } - } - - return image.Sample(textureSampler, pos); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -47591,169 +53039,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRemovePartialPixelsShader { +function Get-OBSPersistentData { -[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] +[Alias('obs.powershell.websocket.GetPersistentData')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the notes of OBSRemovePartialPixelsShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'remove_partial_pixels' -$ShaderNoun = 'OBSRemovePartialPixelsShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int minimum_alpha_percent< - string label = "minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; -float4 mainImage(VertData v_in) : TARGET -{ - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - else - { - return float4(output_color); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -47762,204 +53152,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatGridCenterCropShader { +function Get-OBSProfile { -[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] +[Alias('obs.powershell.websocket.GetProfileList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the alpha of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('alpha')] -[Single] -$Alpha, -# Set the columns of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('columns')] -[Single] -$Columns, -# Set the rows of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('rows')] -[Single] -$Rows, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'repeat_grid_center_crop' -$ShaderNoun = 'OBSRepeatGridCenterCropShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// repeat_grid_center_crop.effect - -uniform float4x4 ViewProj; -uniform texture2d image; -sampler_state def_sampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -uniform float alpha = 1.0; -uniform float columns = 3.0; -uniform float rows = 1.0; -struct VertInOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -VertInOut VSDefault(VertInOut vert_in) { - VertInOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1), ViewProj); - vert_out.uv = vert_in.uv * float2(columns, rows); - return vert_out; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 PSMain(VertInOut vert_in) : TARGET { - // Calculate fractional UV within grid cell - float2 cell_uv = frac(vert_in.uv); - - // Calculate center crop parameters - float horizontalCropStart = 0.5 - 0.5/columns; - - // Map to centered portion of original image - float2 original_uv = float2( - cell_uv.x / columns + horizontalCropStart, - cell_uv.y / rows - ); - - // Sample the image - float4 col = image.Sample(def_sampler, original_uv); - col.a *= alpha; - return col; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -technique Draw { - pass { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSMain(vert_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSProfileParameter { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] +[Alias('obs.powershell.websocket.GetProfileParameter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -47968,267 +53368,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatShader { +function Get-OBSRecordDirectory { -[Alias('Set-OBSRepeatShader','Add-OBSRepeatShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] +[Alias('obs.powershell.websocket.GetRecordDirectory')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSRepeatShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the alpha of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('alpha')] -[Single] -$Alpha, -# Set the copies of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('copies')] -[Single] -$Copies, -# Set the notes of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'repeat' -$ShaderNoun = 'OBSRepeatShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float alpha< - string label = "Alpha"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 4.0; -uniform string notes< - string widget_type = "info"; -> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state def_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertInOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -VertInOut VSDefault(VertInOut vert_in) -{ - VertInOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv = vert_in.uv * sqrt(copies); - return vert_out; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 PSDrawBare(VertInOut vert_in) : TARGET -{ - float4 rgba = image.Sample(def_sampler, vert_in.uv); - rgba.a *= alpha; - return rgba; } -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); - } -} +} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSRecordStatus { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] +[Alias('obs.powershell.websocket.GetRecordStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -48237,300 +53574,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatTextureShader { +function Get-OBSReplayBufferStatus { -[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] +[Alias('obs.powershell.websocket.GetReplayBufferStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatTextureShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatTextureShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatTextureShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the tex_image of OBSRepeatTextureShader -[Alias('tex_image')] -[ComponentModel.DefaultBindingProperty('tex_image')] -[String] -$TexImage, -# Set the elapsed_time of OBSRepeatTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the blend of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('blend')] -[Single] -$Blend, -# Set the copies of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('copies')] -[Single] -$Copies, -# Set the notes of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the alpha_percentage of OBSRepeatTextureShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Single] -$AlphaPercentage, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'repeat_texture' -$ShaderNoun = 'OBSRepeatTextureShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; -uniform texture2d tex_image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float blend< - string label = "Blend"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 4.0; -uniform string notes< - string widget_type = "info"; -> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; -uniform float alpha_percentage< - string label = "alpha percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -sampler_state tex_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -sampler_state base_sampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; +} -struct VertIn { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; -struct VertOut { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; +} -VertOut VSDefault(VertIn vert_in) -{ - VertOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv_1 = vert_in.uv_0; - vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); - return vert_out; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSScene { -float4 PSDrawBare(VertOut vert_in) : TARGET -{ - float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); - float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); - float4 base = image.Sample(base_sampler, vert_in.uv_1); - return (1 - alpha) * base + (alpha) * tex; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] +[Alias('obs.powershell.websocket.GetSceneList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +process { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -48539,199 +53780,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGBAPercentShader { +function Get-OBSSceneCollection { -[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] +[Alias('obs.powershell.websocket.GetSceneCollectionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the RedPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('RedPercent')] -[Single] -$RedPercent, -# Set the GreenPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('GreenPercent')] -[Single] -$GreenPercent, -# Set the BluePercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('BluePercent')] -[Single] -$BluePercent, -# Set the AlphaPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('AlphaPercent')] -[Single] -$AlphaPercent, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'RGBA_Percent' -$ShaderNoun = 'OBSRGBAPercentShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple RGBA Percent Shader -// Allows Red, Green, or Blue to be adjusted between 0-200% -// Allows Alpha to be adjusted between 0/100% -uniform float RedPercent< - string label = "Red percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; -uniform float GreenPercent< - string label = "Green percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; -uniform float BluePercent< - string label = "Blue percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200; - float step = 1.0; -> = 100.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float AlphaPercent< - string label = "Alpha percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - float4 imageColors = image.Sample(textureSampler, v_in.uv); - float4 adjustedColor = float4( - imageColors.r * (RedPercent * 0.01), - imageColors.g * (GreenPercent * 0.01), - imageColors.b * (BluePercent * 0.01), - imageColors.a * (AlphaPercent * 0.01) - ); - return adjustedColor; -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItem { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] +[Alias('obs.powershell.websocket.GetSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -48740,268 +53996,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbColorWheelShader { +function Get-OBSSceneItemBlendMode { -[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the speed of OBSRgbColorWheelShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the color_depth of OBSRgbColorWheelShader -[Alias('color_depth')] -[ComponentModel.DefaultBindingProperty('color_depth')] -[Single] -$ColorDepth, -# Set the Apply_To_Image of OBSRgbColorWheelShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSRgbColorWheelShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSRgbColorWheelShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Alpha_Percentage of OBSRgbColorWheelShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the center_width_percentage of OBSRgbColorWheelShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRgbColorWheelShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rgb_color_wheel' -$ShaderNoun = 'OBSRgbColorWheelShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 15.0; - float step = 0.1; -> = 2.0; -uniform float color_depth< - string label = "Color Depth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 2.10; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100; // -uniform int center_width_percentage< - string label = "center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; - -float3 hsv2rgb(float3 c) -{ - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} -float mod(float x, float y) -{ - return x - y * floor(x / y); -} - -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.14159265f;//acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st ; - float r = length(toCenter) * color_depth; - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(speed ,18)) / 18; - rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemEnabled { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] +[Alias('obs.powershell.websocket.GetSceneItemEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -49010,212 +54234,122 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbSplitShader { +function Get-OBSSceneItemId { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] +[Alias('obs.powershell.websocket.GetSceneItemId')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -[Alias('Set-OBSRgbSplitShader','Add-OBSRgbSplitShader')] -param( -# Set the redx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redx')] -[Single] -$Redx, -# Set the redy of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redy')] -[Single] -$Redy, -# Set the greenx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greenx')] -[Single] -$Greenx, -# Set the greeny of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greeny')] -[Single] -$Greeny, -# Set the bluex of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluex')] -[Single] -$Bluex, -# Set the bluey of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluey')] -[Single] -$Bluey, -# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('searchOffset')] +[ValidateRange(-1,[int]::MaxValue)] +[double] +$SearchOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rgb_split' -$ShaderNoun = 'OBSRgbSplitShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float redx< - string label = "Red X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 2.00; -uniform float redy< - string label = "Red Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greenx< - string label = "Green X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greeny< - string label = "Green Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float bluex< - string label = "Blue X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = -2.00; -uniform float bluey< - string label = "Blue Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - if(redx != 0.0 || redy != 0.0) - c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; - if(greenx != 0.0 || greeny != 0.0) - c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; - if(bluex != 0.0 || bluey != 0.0) - c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -49224,225 +54358,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbvisibilityShader { +function Get-OBSSceneItemIndex { -[Alias('Set-OBSRgbvisibilityShader','Add-OBSRgbvisibilityShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] +[Alias('obs.powershell.websocket.GetSceneItemIndex')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Red of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Red')] -[Single] -$Red, -# Set the Green of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Green')] -[Single] -$Green, -# Set the Blue of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Blue')] -[Single] -$Blue, -# Set the RedVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('RedVisibility')] -[Single] -$RedVisibility, -# Set the GreenVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('GreenVisibility')] -[Single] -$GreenVisibility, -# Set the BlueVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('BlueVisibility')] -[Single] -$BlueVisibility, -# Set the notes of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rgbvisibility' -$ShaderNoun = 'OBSRgbvisibilityShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGB visibility separation filter, created by EposVox - -uniform float Red< - string label = "Red"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; - -uniform float Green< - string label = "Green"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform float Blue< - string label = "Blue"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; - -uniform float RedVisibility< - string label = "Red Visibility"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float GreenVisibility< - string label = "Green Visibility"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float BlueVisibility< - string label = "Blue Visibility"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform string notes< - string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction."; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - float redChannel = pow(c.r, Red) * RedVisibility; - float greenChannel = pow(c.g, Green) * GreenVisibility; - float blueChannel = pow(c.b, Blue) * BlueVisibility; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - return float4(redChannel, greenChannel, blueChannel, c.a); -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemLocked { + - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] +[Alias('obs.powershell.websocket.GetSceneItemLocked')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -49451,291 +54596,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGSSAAShader { +function Get-OBSSceneItemSource { -[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] +[Alias('obs.powershell.websocket.GetSceneItemSource')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ColorSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('ColorSigma')] -[Single] -$ColorSigma, -# Set the SpatialSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('SpatialSigma')] -[Single] -$SpatialSigma, -# Set the notes of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'RGSSAA' -$ShaderNoun = 'OBSRGSSAAShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 -// https://github.com/exeldro/obs-shaderfilter/tree/master -// Using edge detection shader as a base, created by Hallatore -// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 -uniform float ColorSigma< - string label = "Color Sigma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 1.0; - float step = 0.1; -> = 1.0; -uniform float SpatialSigma< - string label = "Spatial Sigma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 1.0; - float step = 0.1; -> = 1.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform string notes< - string widget_type = "info"; -> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float Luminance(float3 rgb) -{ - return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; } -float4 mainImage(VertData v_in) : TARGET -{ - float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; - float2 SceneUV = v_in.uv; - float2 TexelScale = 1.0f/uv_size; - const float SQRT2 = 1.4142135624; - const float PI = 3.141592654; - const float angle = PI / 8.0; - const float cs = cos(angle); - const float sn = sin(angle); +} - // Rotated grid samples - float3 C1 = - image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; - float3 C2 = - image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; - float3 C3 = - image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; - float3 C4 = - image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; - float3 C5 = - image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; - float3 C6 = - image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; - float3 C7 = - image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; - float3 C8 = - image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemTransform { - // Luminance edge detection - float A0 = Luminance(SceneColor); - float CL1 = Luminance(C1); - float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); - float CL2 = Luminance(C2); - float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); - float CL3 = Luminance(C3); - float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); - float CL4 = Luminance(C4); - float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); - float CL5 = Luminance(C5); - float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); - float CL6 = Luminance(C6); - float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); - float CL7 = Luminance(C7); - float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); - float CL8 = Luminance(C8); - float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); - float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); - // Calculate distance-based weights - float2 Dist1 = float2(cs, -sn); - float2 Dist2 = float2(-cs, -sn); - float2 Dist3 = float2(-sn, cs); - float2 Dist4 = float2(sn, cs); - float2 Dist5 = float2(cs * SQRT2, 0); - float2 Dist6 = float2(0, sn * SQRT2); - float2 Dist7 = float2(-cs * SQRT2, 0); - float2 Dist8 = float2(0, -sn * SQRT2); - float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); - float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); - float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); - float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); - float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); - float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); - float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); - float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] +[Alias('obs.powershell.websocket.GetSceneItemTransform')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - // Color weights - float3 ColorDiff1 = SceneColor.rgb - C1; - float3 ColorDiff2 = SceneColor.rgb - C2; - float3 ColorDiff3 = SceneColor.rgb - C3; - float3 ColorDiff4 = SceneColor.rgb - C4; - float3 ColorDiff5 = SceneColor.rgb - C5; - float3 ColorDiff6 = SceneColor.rgb - C6; - float3 ColorDiff7 = SceneColor.rgb - C7; - float3 ColorDiff8 = SceneColor.rgb - C8; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); - float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); - float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); - float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); - float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); - float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); - float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); - float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - // Mixing weights - float W1 = SW1 * CW1; - float W2 = SW2 * CW2; - float W3 = SW3 * CW3; - float W4 = SW4 * CW4; - float W5 = SW5 * CW5; - float W6 = SW6 * CW6; - float W7 = SW7 * CW7; - float W8 = SW8 * CW8; - float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // Weighted color - float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + - C7 * W7 + C8 * W8) / - max(TotalWeight, 0.0001); - // Blend it - float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); - float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); - float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); - float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); - float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); - float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); - float HDREdge = max(A0Max, A0Max2); - float EdgeMask = saturate(1.0f - HDREdge); +process { - return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -49744,202 +54834,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRippleShader { +function Get-OBSSceneSceneTransitionOverride { -[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the distance_factor of OBSRippleShader -[Alias('distance_factor')] -[ComponentModel.DefaultBindingProperty('distance_factor')] -[Single] -$DistanceFactor, -# Set the time_factor of OBSRippleShader -[Alias('time_factor')] -[ComponentModel.DefaultBindingProperty('time_factor')] -[Single] -$TimeFactor, -# Set the power_factor of OBSRippleShader -[Alias('power_factor')] -[ComponentModel.DefaultBindingProperty('power_factor')] -[Single] -$PowerFactor, -# Set the center_pos_x of OBSRippleShader -[Alias('center_pos_x')] -[ComponentModel.DefaultBindingProperty('center_pos_x')] -[Single] -$CenterPosX, -# Set the center_pos_y of OBSRippleShader -[Alias('center_pos_y')] -[ComponentModel.DefaultBindingProperty('center_pos_y')] -[Single] -$CenterPosY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'ripple' -$ShaderNoun = 'OBSRippleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float distance_factor< - string label = "distance factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 12.0; -uniform float time_factor< - string label = "time factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform float power_factor< - string label = "power factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 3.0; -uniform float center_pos_x< - string label = "center pos x"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float center_pos_y< - string label = "center pos y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -float4 mainImage(VertData v_in) : TARGET -{ - float2 cPos = (v_in.uv * 2 ) -1; - float2 center_pos = float2(center_pos_x, center_pos_y); - float cLength = distance(cPos, center_pos); - float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -49948,237 +54947,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatingSourceShader { +function Get-OBSSceneTransition { -[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] +[Alias('obs.powershell.websocket.GetSceneTransitionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the spin_speed of OBSRotatingSourceShader -[Alias('spin_speed')] -[ComponentModel.DefaultBindingProperty('spin_speed')] -[Single] -$SpinSpeed, -# Set the rotation of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('rotation')] -[Single] -$Rotation, -# Set the zoomin of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('zoomin')] -[Single] -$Zoomin, -# Set the keep_aspectratio of OBSRotatingSourceShader -[Alias('keep_aspectratio')] -[ComponentModel.DefaultBindingProperty('keep_aspectratio')] -[Management.Automation.SwitchParameter] -$KeepAspectratio, -# Set the x_center of OBSRotatingSourceShader -[Alias('x_center')] -[ComponentModel.DefaultBindingProperty('x_center')] -[Single] -$XCenter, -# Set the y_center of OBSRotatingSourceShader -[Alias('y_center')] -[ComponentModel.DefaultBindingProperty('y_center')] -[Single] -$YCenter, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rotating-source' -$ShaderNoun = 'OBSRotatingSourceShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//spin speed higher the slower -uniform float spin_speed< - string label = "Spin Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float rotation< - string label = "Rotation"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.1; -> = 0.0; -uniform float zoomin< - string label = "Zoom"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform bool keep_aspectratio = true; -uniform float x_center< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float y_center< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; - -//main fragment code -//from lioran to nutella with love -float4 mainImage(VertData v_in) : TARGET -{ - float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; - float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; - //get position on of the texture and focus on the middle - float i_rotation; - if (spin_speed == 0){ - //turn angle number into pi number - i_rotation = rotation/57.295779513; - }else{ - //use elapsed time for spinning if spin speed is not 0 - i_rotation = elapsed_time * spin_speed; - } - float2 i_point; - i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); - i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); - - //get the angle from center , returns pi number - float i_dir = atan(i_point.y/i_point.x); - if(i_point.x < 0.0){ - i_dir += 3.14159265359; - } - - //get the distance from the centers - float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); - //multiple distance by the zoomin value - i_distance *= zoomin; - - //shift the texture position based on angle and distance from the middle - i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; - i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; - - //draw normally from new point - return image.Sample(textureSampler, i_point); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -50187,374 +55050,229 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatoeShader { +function Get-OBSSourceActive { -[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] +[Alias('obs.powershell.websocket.GetSourceActive')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSRotatoeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSRotatoeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRotatoeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRotatoeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSRotatoeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSRotatoeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the speed_percent of OBSRotatoeShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the Axis_X of OBSRotatoeShader -[Alias('Axis_X')] -[ComponentModel.DefaultBindingProperty('Axis_X')] -[Single] -$AxisX, -# Set the Axis_Y of OBSRotatoeShader -[Alias('Axis_Y')] -[ComponentModel.DefaultBindingProperty('Axis_Y')] -[Single] -$AxisY, -# Set the Axis_Z of OBSRotatoeShader -[Alias('Axis_Z')] -[ComponentModel.DefaultBindingProperty('Axis_Z')] -[Single] -$AxisZ, -# Set the Angle_Degrees of OBSRotatoeShader -[Alias('Angle_Degrees')] -[ComponentModel.DefaultBindingProperty('Angle_Degrees')] -[Single] -$AngleDegrees, -# Set the Rotate_Transform of OBSRotatoeShader -[Alias('Rotate_Transform')] -[ComponentModel.DefaultBindingProperty('Rotate_Transform')] -[Management.Automation.SwitchParameter] -$RotateTransform, -# Set the Rotate_Pixels of OBSRotatoeShader -[Alias('Rotate_Pixels')] -[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] -[Management.Automation.SwitchParameter] -$RotatePixels, -# Set the Rotate_Colors of OBSRotatoeShader -[Alias('Rotate_Colors')] -[ComponentModel.DefaultBindingProperty('Rotate_Colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the center_width_percentage of OBSRotatoeShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRotatoeShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, -# Set the notes of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rotatoe' -$ShaderNoun = 'OBSRotatoeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform int speed_percent< - string label = "speed percentage"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 50; // -uniform float Axis_X< - string label = "Axis X"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.1; -> = 0.0; -uniform float Axis_Y< - string label = "Axis Y"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 0.0; -uniform float Axis_Z< - string label = "Axis Z"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float Angle_Degrees< - string label = "Angle Degrees"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 0.01; -> = 45.0; -uniform bool Rotate_Transform = true; -uniform bool Rotate_Pixels = false; -uniform bool Rotate_Colors = false; -uniform int center_width_percentage< - string label = "center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3x3 rotAxis(float3 axis, float a) { - float s=sin(a); - float c=cos(a); - float oc=1.0-c; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float3 as=axis*s; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); - float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); - return p*oc+q; } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; - - // use matrix to transform rotation - if (Rotate_Transform) - vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; - vert_out.uv = v_in.uv * uv_scale + uv_offset; +} - return vert_out; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceFilter { -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] +[Alias('obs.powershell.websocket.GetSourceFilter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - // use matrix to transform pixels - if (Rotate_Pixels) - { - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); - } - if (Rotate_Colors) - rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, - return rgba; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +process { - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -50563,330 +55281,209 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRect2Shader { +function Get-OBSSourceFilterDefaultSettings { -[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] +[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius of OBSRoundedRect2Shader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedRect2Shader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRect2Shader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRect2Shader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRect2Shader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRect2Shader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# Set the faster_scan of OBSRoundedRect2Shader -[Alias('faster_scan')] -[ComponentModel.DefaultBindingProperty('faster_scan')] -[Management.Automation.SwitchParameter] -$FasterScan, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_rect2' -$ShaderNoun = 'OBSRoundedRect2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Aplha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -uniform bool faster_scan = true; -float4 mainImage(VertData v_in) : TARGET -{ - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - float check_dist = float(corner_radius); - if(border_thickness > check_dist) - check_dist = border_thickness; - if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(-check_dist); - } - if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(-check_dist); - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(!faster_scan || closedEdgeX != 0){ - [loop] for(int x = 1;float(x) check_dist && border_thickness > corner_radius){ - if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ - float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(floor(cd) > corner_radius) - return float4(0.0,0.0,0.0,0.0); - if(cd > corner_radius){ - d = border_thickness + cd - corner_radius; - } else if(d > border_thickness){ - d = border_thickness; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - }else if(d > border_thickness){ - d = border_thickness; } - } - if(floor(d) <= check_dist){ - if(border_thickness > 0){ - if(ceil(check_dist-d) <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - if(border_alpha_start < border_alpha_end){ - fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; - fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - if(d > check_dist) - fade_color.a *= 1.0 -(d - check_dist); - return fade_color; - }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ - float4 fade_color = border_color; - float f; - if(border_thickness > (check_dist-d)) - f = border_thickness - (check_dist-d); - else - f = 1.0 -((check_dist-d) - border_thickness); - fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; - return fade_color; } } - if (d > check_dist) - pixel.a *= 1.0 - (d - check_dist); - return pixel; - } - return float4(0.0,0.0,0.0,0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceFilterKind { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] +[Alias('obs.powershell.websocket.GetSourceFilterKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -50895,354 +55492,247 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectPerCornerShader { +function Get-OBSSourceFilterList { -[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] +[Alias('obs.powershell.websocket.GetSourceFilterList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius_tl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tl')] -[ComponentModel.DefaultBindingProperty('corner_radius_tl')] -[Int32] -$CornerRadiusTl, -# Set the corner_radius_tr of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tr')] -[ComponentModel.DefaultBindingProperty('corner_radius_tr')] -[Int32] -$CornerRadiusTr, -# Set the corner_radius_br of OBSRoundedRectPerCornerShader -[Alias('corner_radius_br')] -[ComponentModel.DefaultBindingProperty('corner_radius_br')] -[Int32] -$CornerRadiusBr, -# Set the corner_radius_bl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_bl')] -[ComponentModel.DefaultBindingProperty('corner_radius_bl')] -[Int32] -$CornerRadiusBl, -# Set the border_thickness of OBSRoundedRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_rect_per_corner' -$ShaderNoun = 'OBSRoundedRectPerCornerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_radius_tl< - string label = "Corner radius top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_tr< - string label = "Corner radius top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_br< - string label = "Corner radius bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_bl< - string label = "Corner radius bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; - int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; - int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; - int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_radius_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_radius_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_radius_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(dAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceScreenshot { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] +[Alias('obs.powershell.websocket.GetSourceScreenshot')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -51251,331 +55741,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectPerSideShader { +function Get-OBSSpecialInputs { -[Alias('Set-OBSRoundedRectPerSideShader','Add-OBSRoundedRectPerSideShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] +[Alias('obs.powershell.websocket.GetSpecialInputs')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius_bottom of OBSRoundedRectPerSideShader -[Alias('corner_radius_bottom')] -[ComponentModel.DefaultBindingProperty('corner_radius_bottom')] -[Int32] -$CornerRadiusBottom, -# Set the corner_radius_left of OBSRoundedRectPerSideShader -[Alias('corner_radius_left')] -[ComponentModel.DefaultBindingProperty('corner_radius_left')] -[Int32] -$CornerRadiusLeft, -# Set the corner_radius_top of OBSRoundedRectPerSideShader -[Alias('corner_radius_top')] -[ComponentModel.DefaultBindingProperty('corner_radius_top')] -[Int32] -$CornerRadiusTop, -# Set the corner_radius_right of OBSRoundedRectPerSideShader -[Alias('corner_radius_right')] -[ComponentModel.DefaultBindingProperty('corner_radius_right')] -[Int32] -$CornerRadiusRight, -# Set the border_thickness of OBSRoundedRectPerSideShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectPerSideShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRectPerSideShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRectPerSideShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRectPerSideShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_rect_per_side' -$ShaderNoun = 'OBSRoundedRectPerSideShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int corner_radius_bottom< - string label = "Corner radius bottom"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_left< - string label = "Corner radius left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_top< - string label = "Corner radius top"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_right< - string label = "Corner radius right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 output_color = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(output_color.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return output_color; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x corner_radius){ - closedEdgeXabs = 0; - } - if(closedEdgeYabs > corner_radius){ - closedEdgeYabs = 0; - } - if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ - return output_color; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - } - float closest = closedEdgeXabsAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStats { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStats')] +[Alias('obs.powershell.websocket.GetStats')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -51584,183 +55947,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectShader { +function Get-OBSStreamServiceSettings { -[Alias('Set-OBSRoundedRectShader','Add-OBSRoundedRectShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] +[Alias('obs.powershell.websocket.GetStreamServiceSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius of OBSRoundedRectShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedRectShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_rect' -$ShaderNoun = 'OBSRoundedRectShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; - -uniform float4 border_color; -float4 mainImage(VertData v_in) : TARGET -{ - float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); - float4 output_color = image.Sample(textureSampler, v_in.uv); - - float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); - float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); - - bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; - bool is_within_radius = pixel_distance_from_center <= corner_radius; - - bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); - bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); - - return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -51769,365 +56050,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeGradientShader { +function Get-OBSStreamStatus { -[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] +[Alias('obs.powershell.websocket.GetStreamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius of OBSRoundedStrokeGradientShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeGradientShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the rotation_speed of OBSRoundedStrokeGradientShader -[Alias('rotation_speed')] -[ComponentModel.DefaultBindingProperty('rotation_speed')] -[Int32] -$RotationSpeed, -# Set the border_colorL of OBSRoundedStrokeGradientShader -[Alias('border_colorL')] -[ComponentModel.DefaultBindingProperty('border_colorL')] -[String] -$BorderColorL, -# Set the border_colorR of OBSRoundedStrokeGradientShader -[Alias('border_colorR')] -[ComponentModel.DefaultBindingProperty('border_colorR')] -[String] -$BorderColorR, -# Set the center_width of OBSRoundedStrokeGradientShader -[Alias('center_width')] -[ComponentModel.DefaultBindingProperty('center_width')] -[Int32] -$CenterWidth, -# Set the center_height of OBSRoundedStrokeGradientShader -[Alias('center_height')] -[ComponentModel.DefaultBindingProperty('center_height')] -[Int32] -$CenterHeight, -# Set the notes of OBSRoundedStrokeGradientShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_stroke_gradient' -$ShaderNoun = 'OBSRoundedStrokeGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int rotation_speed< - string label = "rotation speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_colorL; -uniform float4 border_colorR; -//uniform float color_spread = 2.0; -uniform int center_width< - string label = "center width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height< - string label = "center height"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; - -// float3 hsv2rgb(float3 c) -// { -// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); -// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); -// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -// } - -float mod(float x, float y) -{ - return x - y * floor(x/y); -} -float4 gradient(float c) { - c = mod(c , 2.0); - if(c < 0.0f){ - c = c * -1.0; - } - if(c > 1.0){ - c = 1.0 - c; - if(c < 0.0f){ - c = c + 1.0; - } - } - return lerp(border_colorL, border_colorR, c); -} - -float4 getBorderColor(float2 toCenter){ - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; - return gradient((angle / 3.14159265f) + angleMod); -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 st = v_in.uv * uv_scale; - float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); - float2 toCenter = center_pixel_coordinates - st; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return output_color; - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - return float4(0.0, 0.0, 0.0, 0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStudioModeEnabled { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] +[Alias('obs.powershell.websocket.GetStudioModeEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -52136,287 +56256,307 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeShader { +function Get-OBSTransitionKind { -[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetTransitionKindList')] +[Alias('obs.powershell.websocket.GetTransitionKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_radius of OBSRoundedStrokeShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the border_color of OBSRoundedStrokeShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the notes of OBSRoundedStrokeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'rounded_stroke' -$ShaderNoun = 'OBSRoundedStrokeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float4 border_color; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; -float4 mainImage(VertData v_in) : TARGET -{ - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return float4(output_color); - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return border_color; - } - else - { - return float4(output_color); + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return border_color; + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - else - { - return float4(output_color); + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return border_color; - } - else - { - return output_color; +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSVersion { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVersion')] +[Alias('obs.powershell.websocket.GetVersion')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - return float4(0.0, 0.0, 0.0, 0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSVideoSettings { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] +[Alias('obs.powershell.websocket.GetVideoSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -52425,265 +56565,213 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScanLineShader { +function Get-OBSVirtualCamStatus { -[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] +[Alias('obs.powershell.websocket.GetVirtualCamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the lengthwise of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('lengthwise')] -[Management.Automation.SwitchParameter] -$Lengthwise, -# Set the animate of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the speed of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the angle of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the shift of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('shift')] -[Management.Automation.SwitchParameter] -$Shift, -# Set the boost of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('boost')] -[Management.Automation.SwitchParameter] -$Boost, -# Set the floor of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('floor')] -[Single] -$Floor, -# Set the period of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('period')] -[Single] -$Period, -# Set the notes of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'scan_line' -$ShaderNoun = 'OBSScanLineShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Scan Line Effect for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -//Count the number of scanlines we want via height or width, adjusts the sin wave period -uniform bool lengthwise; -//Do we want the scanlines to move? -uniform bool animate; -//How fast do we want those scanlines to move? -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10000.0; - float step = 1; -> = 1000; -//What angle should the scanlines come in at (based in degrees) -uniform float angle< - string label = "angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 45; -//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit -//By default values for color range from 0 to 1 -//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen -uniform bool shift = true; -uniform bool boost = true; -//Increases the minimum value of the sin wave -uniform float floor< - string label = "Floor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 0.0; -//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input -uniform float period< - string label = "Period"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 1.0; -> = 10.0; -uniform string notes< - string widget_type = "info"; -> = "floor affects the minimum opacity of the scan line"; -float4 mainImage(VertData v_in) : TARGET -{ - //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 - // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 - float nfloor = clamp(floor, 0.0, 100.0) * 0.01; - float nperiod = max(period, 1.0); - float gap = 1 - nfloor; - float pi = 3.1415926535897932384626; - float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); - float nspeed = 0.0; - if(animate){ - nspeed = speed * 0.0001; - } - - float4 color = image.Sample(textureSampler, v_in.uv); - - float t = elapsed_time * nspeed; - - if(!lengthwise){ - float base_height = 1.0 / uv_pixel_interval.y; - float h_interval = pi * base_height; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } - else{ - float base_width = 1.0 / uv_pixel_interval.x; - float w_interval = pi * base_width; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSInputFiltersDialog { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] +[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -52692,449 +56780,344 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeascapeShader { +function Open-OBSInputInteractDialog { -[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] +[Alias('obs.powershell.websocket.OpenInputInteractDialog')] param( -# Set the AA of OBSSeascapeShader -[ComponentModel.DefaultBindingProperty('AA')] -[Management.Automation.SwitchParameter] -$AA, -# Set the SEA_HEIGHT of OBSSeascapeShader -[Alias('SEA_HEIGHT')] -[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] -[Single] -$SEAHEIGHT, -# Set the SEA_CHOPPY of OBSSeascapeShader -[Alias('SEA_CHOPPY')] -[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] -[Single] -$SEACHOPPY, -# Set the SEA_SPEED of OBSSeascapeShader -[Alias('SEA_SPEED')] -[ComponentModel.DefaultBindingProperty('SEA_SPEED')] -[Single] -$SEASPEED, -# Set the SEA_FREQ of OBSSeascapeShader -[Alias('SEA_FREQ')] -[ComponentModel.DefaultBindingProperty('SEA_FREQ')] -[Single] -$SEAFREQ, -# Set the SEA_BASE of OBSSeascapeShader -[Alias('SEA_BASE')] -[ComponentModel.DefaultBindingProperty('SEA_BASE')] -[String] -$SEABASE, -# Set the SEA_WATER_COLOR of OBSSeascapeShader -[Alias('SEA_WATER_COLOR')] -[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] -[String] -$SEAWATERCOLOR, -# Set the CAMERA_SPEED of OBSSeascapeShader -[Alias('CAMERA_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] -[Single] -$CAMERASPEED, -# Set the CAMERA_TURN_SPEED of OBSSeascapeShader -[Alias('CAMERA_TURN_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] -[Single] -$CAMERATURNSPEED, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'seascape' -$ShaderNoun = 'OBSSeascapeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * "Seascape" by Alexander Alekseev aka TDM - 2014 - * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - * Contact: tdmaav@gmail.com - * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro - */ -#define NUM_STEPS 8 -#define PI 3.141592 -#define EPSILON 0.001 -uniform bool AA< - string label = "Smooth (more resources)"; -> = false; -#ifndef OPENGL -#define mat2 float2x2 -#define mat3 float3x3 -#define fract frac -#define mix lerp -#endif + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// sea -#define ITER_GEOMETRY 3 -#define ITER_FRAGMENT 5 -uniform float SEA_HEIGHT< - string label = "Sea Height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.5; - float step = 0.001; -> = 0.6; -uniform float SEA_CHOPPY< - string label = "Sea Choppy"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 4.0; -uniform float SEA_SPEED< - string label = "Sea Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.8; -uniform float SEA_FREQ< - string label = "Sea Frequency"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.16; -uniform float4 SEA_BASE< - string label = "Sea Base"; -> = {0.0,0.09,0.18,1.0}; -uniform float4 SEA_WATER_COLOR< - string label = "Sea Water"; -> = {0.48,0.54,0.36,1.0}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float CAMERA_SPEED< - string label = "Camera Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float CAMERA_TURN_SPEED< - string label = "Camera Turn Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float SEA_TIME(){ - return 1.0 + elapsed_time * SEA_SPEED; } -// math -mat3 fromEuler(float3 ang) { - float2 a1 = float2(sin(ang.x),cos(ang.x)); - float2 a2 = float2(sin(ang.y),cos(ang.y)); - float2 a3 = float2(sin(ang.z),cos(ang.z)); - return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), - float3(-a2.y*a1.x,a1.y*a2.y,a2.x), - float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); -} -float hash(float2 p) { - float h = dot(p,float2(127.1,311.7)); - return fract(sin(h)*43758.5453123); -} +} -float noise(float2 p) { - float2 i = floor( p ); - float2 f = fract( p ); - float2 u = f*f*(3.0-2.0*f); - return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), - hash( i + float2(1.0,0.0) ), u.x), - mix( hash( i + float2(0.0,1.0) ), - hash( i + float2(1.0,1.0) ), u.x), u.y); -} + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSInputPropertiesDialog { -// lighting -float diffuse(float3 n,float3 l,float p) { - return pow(dot(n,l) * 0.4 + 0.6,p); -} -float specular(float3 n,float3 l,float3 e,float s) { - float nrm = (s + 8.0) / (PI * 8.0); - return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; -} -// sky -float3 getSkyColor(float3 e) { - e.y = (max(e.y,0.0)*0.8+0.2)*0.8; - return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] +[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } -// sea -float sea_octave(float2 uv, float choppy) { - uv += noise(uv); - float2 wv = 1.0-abs(sin(uv)); - float2 swv = abs(cos(uv)); - wv = mix(wv,swv,wv); - return pow(1.0-pow(wv.x * wv.y,0.65),choppy); -} -float map(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; - uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); +} - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_GEOMETRY; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); - } - return p.y - h; -} + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSSourceProjector { -float map_detailed(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_FRAGMENT; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); - } - return p.y - h; -} -float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { - float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); - fresnel = min(pow(fresnel,3.0), 0.5); - - float3 reflected = getSkyColor(reflect(eye,n)); - float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; - - float3 color = mix(refracted,reflected,fresnel); - - float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); - color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; - - float s = specular(n,l,eye,60.0); - color += float3(s,s,s); - - return color; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] +[Alias('obs.powershell.websocket.OpenSourceProjector')] +param( -// tracing -float3 getNormal(float3 p, float eps) { - float3 n; - n.y = map_detailed(p); - n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; - n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; - n.y = eps; - return normalize(n); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -float heightMapTracing(float3 ori, float3 dir, out float3 p) { - float tm = 0.0; - float tx = 1000.0; - float hx = map(ori + dir * tx); - if(hx > 0.0) { - p = ori + dir * tx; - return tx; - } - float hm = map(ori + dir * tm); - float tmid = 0.0; - for(int i = 0; i < NUM_STEPS; i++) { - tmid = mix(tm,tx, hm/(hm-hx)); - p = ori + dir * tmid; - float hmid = map(p); - if(hmid < 0.0) { - tx = tmid; - hx = hmid; - } else { - tm = tmid; - hm = hmid; - } - } - return tmid; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -float3 getPixel(in float2 coord, float time) { - float2 uv = coord / uv_size.xy; - uv = uv * 2.0 - 1.0; - uv.x *= uv_size.x / uv_size.y; - - // ray - float3 ang = float3(sin(time*3.0*CAMERA_TURN_SPEED)*0.1,sin(time*CAMERA_TURN_SPEED)*0.2+0.3,time*CAMERA_TURN_SPEED); - float3 ori = float3(0.0,3.5,time*5.0*CAMERA_SPEED); - float3 dir = normalize(float3(uv.xy,-2.0)); - dir.z += length(uv) * 0.14; - dir = mul(normalize(dir), fromEuler(ang)); - - // tracing - float3 p; - heightMapTracing(ori,dir,p); - float3 dist = p - ori; - float3 n = getNormal(p, dot(dist,dist) * (0.1 / uv_size.x)); - float3 light = normalize(float3(0.0,1.0,0.8)); - - // color - return mix( - getSkyColor(dir), - getSeaColor(p,n,light,dir,dist), - pow(smoothstep(0.0,-0.02,dir.y),0.2)); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, -// main -float4 mainImage(VertData v_in) : TARGET -{ - float time = elapsed_time * 0.3; - float2 fragCoord = float2(v_in.uv.x * uv_size.x, (1.0 - v_in.uv.y) * uv_size.y); - - float3 color = float3(0.0,0.0,0.0);; - if (AA){ - for(int i = -1; i <= 1; i++) { - for(int j = -1; j <= 1; j++) { - float2 uv = fragCoord+float2(i,j)/3.0; - color += getPixel(uv, time); - } - } - color /= 9.0; - }else{ - color = getPixel(fragCoord, time); - } - - // post - return float4(pow(color,float3(0.65,0.65,0.65)), 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -53143,194 +57126,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeasickShader { +function Open-OBSVideoMixProjector { -[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] +[Alias('obs.powershell.websocket.OpenVideoMixProjector')] param( -# Set the notes of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the amplitude of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the speed of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the frequency of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('frequency')] -[Single] -$Frequency, -# Set the opacity of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('videoMixType')] +[string] +$VideoMixType, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'seasick' -$ShaderNoun = 'OBSSeasickShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Seasick - an effect for OBS Studio -// -uniform string notes< - string widget_type = "info"; -> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; -uniform float amplitude< - string label = "amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.03; -uniform float speed< - string label = "speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float frequency< - string label = "frequency"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 6.0; -uniform float opacity< - string label = "opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); - float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); - return image.Sample(textureSampler, coord) * opacity; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -53339,305 +57243,329 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSelectiveColorShader { +function Remove-OBSInput { -[Alias('Set-OBSSelectiveColorShader','Add-OBSSelectiveColorShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] +[Alias('obs.powershell.websocket.RemoveInput')] param( -# Set the cutoff_Red of OBSSelectiveColorShader -[Alias('cutoff_Red')] -[ComponentModel.DefaultBindingProperty('cutoff_Red')] -[Single] -$CutoffRed, -# Set the cutoff_Green of OBSSelectiveColorShader -[Alias('cutoff_Green')] -[ComponentModel.DefaultBindingProperty('cutoff_Green')] -[Single] -$CutoffGreen, -# Set the cutoff_Blue of OBSSelectiveColorShader -[Alias('cutoff_Blue')] -[ComponentModel.DefaultBindingProperty('cutoff_Blue')] -[Single] -$CutoffBlue, -# Set the cutoff_Yellow of OBSSelectiveColorShader -[Alias('cutoff_Yellow')] -[ComponentModel.DefaultBindingProperty('cutoff_Yellow')] -[Single] -$CutoffYellow, -# Set the acceptance_Amplifier of OBSSelectiveColorShader -[Alias('acceptance_Amplifier')] -[ComponentModel.DefaultBindingProperty('acceptance_Amplifier')] -[Single] -$AcceptanceAmplifier, -# Set the show_Red of OBSSelectiveColorShader -[Alias('show_Red')] -[ComponentModel.DefaultBindingProperty('show_Red')] -[Management.Automation.SwitchParameter] -$ShowRed, -# Set the show_Green of OBSSelectiveColorShader -[Alias('show_Green')] -[ComponentModel.DefaultBindingProperty('show_Green')] -[Management.Automation.SwitchParameter] -$ShowGreen, -# Set the show_Blue of OBSSelectiveColorShader -[Alias('show_Blue')] -[ComponentModel.DefaultBindingProperty('show_Blue')] -[Management.Automation.SwitchParameter] -$ShowBlue, -# Set the show_Yellow of OBSSelectiveColorShader -[Alias('show_Yellow')] -[ComponentModel.DefaultBindingProperty('show_Yellow')] -[Management.Automation.SwitchParameter] -$ShowYellow, -# Set the notes of OBSSelectiveColorShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the background_type of OBSSelectiveColorShader -[Alias('background_type')] -[ComponentModel.DefaultBindingProperty('background_type')] -[Int32] -$BackgroundType, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'selective_color' -$ShaderNoun = 'OBSSelectiveColorShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Selective Color shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//updated 4/13/2020: take into account the opacity/alpha of input image -thanks Skeletonbow for suggestion -//Converted to OpenGL by Q-mii February 25, 2020 -uniform float cutoff_Red< - string label = "cutoff Red"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.40; -uniform float cutoff_Green< - string label = "cutoff Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.025; -uniform float cutoff_Blue< - string label = "cutoff Blue"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float cutoff_Yellow< - string label = "cutoff Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float acceptance_Amplifier< - string label = "acceptance Amplifier"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 5.0; -uniform bool show_Red = true; -uniform bool show_Green = true; -uniform bool show_Blue = true; -uniform bool show_Yellow = true; -uniform string notes< - string widget_type = "info"; -> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; -uniform int background_type< - string label = "background type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Grey"; - int option_1_value = 1; - string option_1_label = "Luma"; - int option_2_value = 2; - string option_2_label = "White"; - int option_3_value = 3; - string option_3_label = "Black"; - int option_4_value = 4; - string option_4_label = "Transparent"; - int option_5_value = 5; - string option_5_label = "Background Color"; -> = 0; -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.1415926535897932384626433832795;//acos(-1); - const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); - float4 color = image.Sample(textureSampler, v_in.uv); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float luminance = dot(coefLuma, color.rgb); - float4 gray = float4(luminance, luminance, luminance, 1.0); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - if (background_type == 0) - { - luminance = (color.r + color.g + color.b) * 0.3333; - gray = float4(luminance,luminance,luminance, 1.0); - } - //if (background_type == 1) - //{ - // gray = float4(luminance,luminance,luminance, 1.0); - //} - if (background_type == 2) - gray = float4(1.0,1.0,1.0,1.0); - if (background_type == 3) - gray = float4(0.0,0.0,0.0,1.0); - if (background_type == 4) - gray.a = 0.01; - if (background_type == 5) - gray = color; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); - float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); - float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); - - float rgLuminance = (color.r*1.4 + color.g*0.6)/2; - float rgDiff = abs(color.r-color.g)*1.4; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float4 accept; - accept.r = float(show_Red) * (redness - cutoff_Red); - accept.g = float(show_Green) * (greenness - cutoff_Green); - accept.b = float(show_Blue) * (blueness - cutoff_Blue); - accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); +} - float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); - float modAcceptance = min (acceptance * acceptance_Amplifier, 1); - float4 result = color; - if (result.a > 0) { - result = modAcceptance * color + (1.0 - modAcceptance) * gray; - //result.a *= gray.a; - } +} - return result; -} + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSProfile { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] +[Alias('obs.powershell.websocket.RemoveProfile')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSScene { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] +[Alias('obs.powershell.websocket.RemoveScene')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -53646,346 +57574,233 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShakeShader { +function Remove-OBSSceneItem { -[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] +[Alias('obs.powershell.websocket.RemoveSceneItem')] param( -# Set the ViewProj of OBSShakeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSShakeShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSShakeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSShakeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSShakeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSShakeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSShakeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSShakeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the local_time of OBSShakeShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the random_scale of OBSShakeShader -[Alias('random_scale')] -[ComponentModel.DefaultBindingProperty('random_scale')] -[Single] -$RandomScale, -# Set the worble of OBSShakeShader -[ComponentModel.DefaultBindingProperty('worble')] -[Management.Automation.SwitchParameter] -$Worble, -# Set the speed of OBSShakeShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSShakeShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSShakeShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] -[Single] -$MaxGrowthPixels, -# Set the randomize_movement of OBSShakeShader -[Alias('randomize_movement')] -[ComponentModel.DefaultBindingProperty('randomize_movement')] -[Management.Automation.SwitchParameter] -$RandomizeMovement, -# Set the notes of OBSShakeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'shake' -$ShaderNoun = 'OBSShakeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -// Added some randomization based upon random_scale input -// updated for latest version of obs-shaderfilter - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform float local_time; -uniform float random_scale< - string label = "random scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.25; -uniform bool worble = false; -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = -2.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform bool randomize_movement = false; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform string notes< - string widget_type = "info"; -> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -//noise values in range if 0.0 to 1.0 + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float noise3D(float x, float y, float z) { - float ptr = 0.0f; - return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - - float3 pos = v_in.pos.xyz; - float t; - float s; - float noise; - if (randomize_movement) - { - t = (rand_f * 2) - 1.0f; - s = (1 - rand_f * 2) - 1.0f;; - noise = clamp( rand_f * random_scale,-0.99, 0.99); - } - else - { - t = (1 + sin(elapsed_time * speed)) / 2; - s = (1 + cos(elapsed_time * speed)) / 2; - noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); - } - - float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); - float3 min_pos; - float3 max_pos; - if (worble) - { - min_pos = pos + direction_from_center * min_growth_pixels * 0.5; - max_pos = pos + direction_from_center * max_growth_pixels * 0.5; - } - else - { - min_pos = pos + direction_from_center * 0.5; - max_pos = min_pos; - } - float3 current_pos = min_pos * (1 - t) + max_pos * t; - //current_pos.x = v_in.pos.x + (t * min_pos.x); - current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); - //current_pos.y = v_in.pos.y + (s * min_pos.y); - //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; +} - float2 offset = uv_offset; - offset.x = uv_offset.x * (1 - t + noise); - offset.y = uv_offset.y * (1 - s + noise); + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSSourceFilter { - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - - //float2 scale = uv_scale; - //scale += dot(pos - current_pos, 1); - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] +[Alias('obs.powershell.websocket.RemoveSourceFilter')] +param( -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -53994,371 +57809,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShineShader { +function Resume-OBSRecord { -[Alias('Set-OBSShineShader','Add-OBSShineShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] +[Alias('obs.powershell.websocket.ResumeRecord')] param( -# Set the l_tex of OBSShineShader -[Alias('l_tex')] -[ComponentModel.DefaultBindingProperty('l_tex')] -[String] -$LTex, -# Set the shine_color of OBSShineShader -[Alias('shine_color')] -[ComponentModel.DefaultBindingProperty('shine_color')] -[String] -$ShineColor, -# Set the speed_percent of OBSShineShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the gradient_percent of OBSShineShader -[Alias('gradient_percent')] -[ComponentModel.DefaultBindingProperty('gradient_percent')] -[Int32] -$GradientPercent, -# Set the delay_percent of OBSShineShader -[Alias('delay_percent')] -[ComponentModel.DefaultBindingProperty('delay_percent')] -[Int32] -$DelayPercent, -# Set the Apply_To_Alpha_Layer of OBSShineShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the ease of OBSShineShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the hide of OBSShineShader -[ComponentModel.DefaultBindingProperty('hide')] -[Management.Automation.SwitchParameter] -$Hide, -# Set the reverse of OBSShineShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the One_Direction of OBSShineShader -[Alias('One_Direction')] -[ComponentModel.DefaultBindingProperty('One_Direction')] -[Management.Automation.SwitchParameter] -$OneDirection, -# Set the glitch of OBSShineShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSShineShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the start_adjust of OBSShineShader -[Alias('start_adjust')] -[ComponentModel.DefaultBindingProperty('start_adjust')] -[Single] -$StartAdjust, -# Set the stop_adjust of OBSShineShader -[Alias('stop_adjust')] -[ComponentModel.DefaultBindingProperty('stop_adjust')] -[Single] -$StopAdjust, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'shine' -$ShaderNoun = 'OBSShineShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -// use color to control shine amount, use transition wipes or make your own alpha texture -// slerp not currently used, for circular effects -//Converted to OpenGL by Exeldro February 14, 2022 -uniform texture2d l_tex; -uniform float4 shine_color ; -uniform int speed_percent< - string label = "speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 25; -uniform int gradient_percent< - string label = "gradient percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; -uniform int delay_percent< - string label = "delay percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool ease = false; -uniform bool hide = false; -uniform bool reverse = false; -uniform bool One_Direction = true; -uniform bool glitch = false; -uniform string notes< - string widget_type = "info"; -> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; - -uniform float start_adjust; -uniform float stop_adjust; - -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; -} - -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; -} - -float4 convert_pmalpha(float4 c) -{ - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} - -float4 slerp(float4 start, float4 end, float percent) -{ - // Dot product - the cosine of the angle between 2 vectors. - float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; - // Clamp it to be in the range of Acos() - // This may be unnecessary, but floating point - // precision can be a fickle mistress. - dotf = clamp(dotf, -1.0f, 1.0f); - // Acos(dot) returns the angle between start and end, - // And multiplying that by percent returns the angle between - // start and the final result. - float theta = acos(dotf)*percent; - float4 RelativeVec = normalize(end - start * dotf); - - // Orthonormal basis - // The final result. - return ((start*cos(theta)) + (RelativeVec*sin(theta))); -} -float4 mainImage(VertData v_in) : TARGET -{ - // convert input for vector math - float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); - float speed = speed_percent * 0.01; - float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); - float delay = clamp(delay_percent * 0.01, 0.0, 1.0); - - // circular easing variable - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = abs(sin(elapsed_time * speed)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - // if time is greater than direction, we are going up! - direction = t - direction; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - // split into segments with frac or mod. - // delay is the gap between starting and ending of the sine wave, use speed to compensate - t = (frac(t) - delay) * (1 / (1 - delay)); - t = 1 + max(t,0.0); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float s = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - //if Unidirectional disable on return - if (One_Direction && (direction < 0.0)) - { - s = 0; - } - else - { - s = Styler(t, 0, c, d, ease); - } +} - // combine luma texture and user defined shine color - float luma = l_tex.Sample(textureSampler, v_in.uv).x; - // - adjust for min and max - if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) - { +} - if (reverse) - { - luma = 1.0 - luma; - } - - // user color with luma - float4 output_color = float4(shine_color.rgb, luma); + +#.ExternalHelp obs-powershell-Help.xml +function Save-OBSReplayBuffer { - float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); - // use luma texture to add alpha and shine +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] +[Alias('obs.powershell.websocket.SaveReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // if behind glow, consider trailing gradient shine then show underlying image - if (luma <= time - softness) - { - float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); - if (Apply_To_Alpha_Layer) - alpha_behind *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_behind); - } - // if in front of glow, consider if the underlying image is hidden - if (luma >= time) - { - // if hide, make the transition better - if (hide) - { - return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); - } - else - { - return rgba; - } - } +process { - // else show the glow area, with luminance - float alpha_ = (time - luma) / softness; - if (Apply_To_Alpha_Layer) - alpha_ *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_); - } - else - { - return rgba; - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -54367,288 +58013,263 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimpleGradientShader { +function Save-OBSSourceScreenshot { -[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] +[Alias('obs.powershell.websocket.SaveSourceScreenshot')] param( -# Set the speed_percentage of OBSSimpleGradientShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSSimpleGradientShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Lens_Flair of OBSSimpleGradientShader -[Alias('Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Lens_Flair')] -[Management.Automation.SwitchParameter] -$LensFlair, -# Set the Animate_Lens_Flair of OBSSimpleGradientShader -[Alias('Animate_Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] -[Management.Automation.SwitchParameter] -$AnimateLensFlair, -# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSSimpleGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSSimpleGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the notes of OBSSimpleGradientShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFilePath')] +[string] +$ImageFilePath, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'simple_gradient' -$ShaderNoun = 'OBSSimpleGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -//lots of room to play here -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform int speed_percentage< - string label = "speed percentage"; - string widget_type = "slider"; - int minimum = -500; - int maximum = 500; - int step = 1; -> = 240; // -uniform int alpha_percentage< - string label = "aplha percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform bool Lens_Flair = false; -uniform bool Animate_Lens_Flair = false; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string notes< - string widget_type = "info"; -> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; -float4 mainImage(VertData v_in) : TARGET -{ + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float4 background_color = image.Sample(textureSampler, v_in.uv); - int no_colors = 4; - float3 colors[4]; - colors[0] = float3(1.0,0.0,0.0); - colors[1] = float3(0.0,1.0,0.0); - colors[2] = float3(0.0,0.0,1.0); - colors[3] = float3(1.0,1.0,1.0); - float alpha = float(alpha_percentage) * 0.01; - float speed = float(speed_percentage) * 0.01; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float mx = max(uv_size.x , uv_size.y); - //float2 uv = v_in.uv / mx; - float3 rgb = background_color.rgb; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(background_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - rgb = float3(v_in.uv.x, v_in.uv.y, 0.10 + 0.85 * sin(elapsed_time * speed)); - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - //create lens flare like effect - if (Lens_Flair) - { - float2 lens_flair_coordinates = float2(0.95 ,0.95); - if (Animate_Lens_Flair) - lens_flair_coordinates *= float2(sin(elapsed_time * speed) ,cos(elapsed_time * speed)); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float dist = distance(v_in.uv, ( lens_flair_coordinates * uv_scale + uv_offset)); - for (int i = 0; i < no_colors; ++i) { - rgb += lerp(rgb, colors[i], dist * 1.5) * 0.25; - } - } + Get-Item $paramCopy["imageFilePath"] | + Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | + Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru + +} - //float3 col = colors[0]; -/* for (int i = 1; i < no_colors; ++i) { - float3 hole = float3( - sin(1.5 - distance(v_in.uv.x / mx, colors[i].x / mx) * 2.5 * speed), - sin(1.5 - distance(v_in.uv.y / mx, colors[i].y / mx) * 2.5 * speed), - colors[i].z); - rgb = lerp(rgb, hole, 0.1); -*/ -/* float3 hole = lerp(colors[i-1], colors[i], sin(elapsed_time * speed)); - col = lerp(col, hole, v_in.uv.x); -*/ - //} -// rgb = fflerp(rgb, col, 0.5); +} + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSCallVendorRequest { - //try prepositioned colors with colors[] array timing - //creates an animated color spotlight -/* int color_index = int(sin(elapsed_time * speed) * no_colors); - float3 start_color = colors[color_index]; - float3 end_color; - if (color_index >= 0) - { - end_color = colors[color_index - 1]; - } - else - { - end_color = colors[no_colors - 1]; - } - rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); -*/ - float4 rgba; - if (Apply_To_Alpha_Layer == false) - { - rgba = lerp(background_color,float4(rgb, 1.0),alpha); - } - else - { - rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); - } - if (Apply_To_Specific_Color) - { - float4 original_color = background_color; - background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; - rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); - } - return rgba; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] +[Alias('obs.powershell.websocket.CallVendorRequest')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('vendorName')] +[string] +$VendorName, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('requestType')] +[string] +$RequestType, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('requestData')] +[PSObject] +$RequestData, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -54657,370 +58278,324 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimplexNoiseShader { +function Send-OBSCustomEvent { -[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] +[Alias('obs.powershell.websocket.BroadcastCustomEvent')] param( -# Set the Snap_Percent of OBSSimplexNoiseShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] -[Single] -$SnapPercent, -# Set the Speed_Percent of OBSSimplexNoiseShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Single] -$SpeedPercent, -# Set the Resolution of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Resolution')] -[Single] -$Resolution, -# Set the Fractal of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Fractal')] -[Management.Automation.SwitchParameter] -$Fractal, -# Set the Use_Alpha_Layer of OBSSimplexNoiseShader -[Alias('Use_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$UseAlphaLayer, -# Set the Fore_Color of OBSSimplexNoiseShader -[Alias('Fore_Color')] -[ComponentModel.DefaultBindingProperty('Fore_Color')] -[String] -$ForeColor, -# Set the Back_Color of OBSSimplexNoiseShader -[Alias('Back_Color')] -[ComponentModel.DefaultBindingProperty('Back_Color')] -[String] -$BackColor, -# Set the Alpha_Percent of OBSSimplexNoiseShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Notes of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('eventData')] +[PSObject] +$EventData, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'simplex_noise' -$ShaderNoun = 'OBSSimplexNoiseShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -//based upon: https://www.shadertoy.com/view/XsX3zB -#ifndef OPENGL -#define fract frac -#endif -uniform float Snap_Percent< - string label = "Snap Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 7.5; -uniform float Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 2.5; -uniform float Resolution< - string label = "Resolution"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 16.0; -uniform bool Fractal = false; -uniform bool Use_Alpha_Layer = false; -uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; -uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; -uniform float Alpha_Percent< - string label = "Alpha Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform string Notes< - string widget_type = "info"; -> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float dot(float3 a, float3 b){ - return a.r*b.r+a.g*b.g+a.b*b.b; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float dot4(float4 a, float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; -} -float snap(float x, float snap) -{ - return snap * round(x / max(0.01,snap)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 random3(float3 co) -{ - float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = fract(512.0 * j); - j *= .125; - result.x = fract(512.0 * j); - j *= .125; - result.y = fract(512.0 * j); - return result - 0.5; } -/* 3d simplex noise */ -float simplex3d(float3 p) { - /* 1. find current tetrahedron T and it''s four vertices */ - /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ - /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ - - /* skew constants for 3d simplex functions */ - float F3 = 0.3333333; - float G3 = 0.1666667; - /* calculate s and x */ - float3 s = floor(p + dot(p, float3(F3,F3,F3))); - float3 x = p - s + dot(s, float3(G3,G3,G3)); +} - /* calculate i1 and i2 */ - float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); - float3 i1 = e * (1.0 - e.zxy); - float3 i2 = 1.0 - e.zxy * (1.0 - e); + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSOffsetMediaInputCursor { - /* x1, x2, x3 */ - float3 x1 = x - i1 + G3; - float3 x2 = x - i2 + 2.0 * G3; - float3 x3 = x - 1.0 + 3.0 * G3; - /* 2. find four surflets and store them in d */ - float4 w, d; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] +[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +param( - /* calculate surflet weights */ - w.x = dot(x, x); - w.y = dot(x1, x1); - w.z = dot(x2, x2); - w.w = dot(x3, x3); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ - w = max(0.61 - w, 0.0); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - /* calculate surflet components */ - d.x = dot(random3(s), x); - d.y = dot(random3(s + i1), x1); - d.z = dot(random3(s + i2), x2); - d.w = dot(random3(s + 1.0), x3); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] +[double] +$MediaCursorOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - /* multiply d by w^4 */ - w *= w; - w *= w; - d *= w; - /* 3. return the sum of the four surflets */ - return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); -} +process { -/* directional artifacts can be reduced by rotating each octave */ -float simplex3d_fractal(float3 m3) { - /* const matrices for 3d rotation */ -#ifdef OPENGL - float3x3 rot1 = float3x3( - float3(-0.37, 0.36, 0.85), - float3(-0.14, -0.93, 0.34), - float3(0.92, 0.01, 0.4 )); - float3x3 rot2 = float3x3( - float3(-0.55, -0.39, 0.74), - float3(0.33, -0.91, -0.24), - float3(0.77, 0.12, 0.63 )); - float3x3 rot3 = float3x3( - float3(-0.71, 0.52, -0.47), - float3(-0.08, -0.72, -0.68), - float3(-0.7, -0.45, 0.56 )); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float3 m = float3(m3.x, m3.y, m3.z); -#else - float3x3 rot1 = { - -0.37, 0.36, 0.85, - -0.14, -0.93, 0.34, - 0.92, 0.01, 0.4 }; - float3x3 rot2 = { - -0.55, -0.39, 0.74, - 0.33, -0.91, -0.24, - 0.77, 0.12, 0.63 }; - float3x3 rot3 = { - -0.71, 0.52, -0.47, - -0.08, -0.72, -0.68, - -0.7, -0.45, 0.56 }; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float3 m = {m3.x, m3.y, m3.z}; -#endif + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - return 0.5333333* simplex3d(mul(m, rot1)) - + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) - + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) - + 0.0666667 * simplex3d(8.0 * m); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float4 mainImage(VertData v_in) : TARGET -{ - float time = snap(elapsed_time, Snap_Percent * .01); - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 p = v_in.uv.xy + float2( 0, -0.5); - float3 p3 = float3(p, time * (Speed_Percent * 0.01)); - - float pixel_alpha = 1.0; - // apply to mainImage rgba - if (Use_Alpha_Layer) { - p3 *= rgba.rgb; - pixel_alpha = rgba.a; - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float value; +} - if (Fractal) { - value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); - } - else { - value = simplex3d(p3 * Resolution); - } - //soften color - value = 0.5 + (0.5 * value); - float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); +} - //use intensity to apply foreground and background colors - float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); - r = lerp(Back_Color, r, saturate(intensity)); - r.a = pixel_alpha; + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSPauseRecord { - return lerp(rgba, r, Alpha_Percent * 0.01); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] +[Alias('obs.powershell.websocket.PauseRecord')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -55029,222 +58604,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSmartDenoiseShader { +function Send-OBSPressInputPropertiesButton { -[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] +[Alias('obs.powershell.websocket.PressInputPropertiesButton')] param( -# Set the uSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uSigma')] -[Single] -$USigma, -# Set the uKSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uKSigma')] -[Single] -$UKSigma, -# Set the uThreshold of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uThreshold')] -[Single] -$UThreshold, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'smart_denoise' -$ShaderNoun = 'OBSSmartDenoiseShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) -// Converted to OBS version of HLSL by Euiko on February 10, 2025 - -#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI -#define INV_PI 0.31830988618379067153776752674503 - -uniform float uSigma< - string label = "Sigma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3; // max based on the webgl sample, which is 3 - float step = 0.01; -> = 5.0; // default value based on shadertoy -uniform float uKSigma< - string label = "K-Sigma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 24; // max based on the webgl sample, which is 24 - float step = 0.01; -> = 7.0; // the default value is based on the webgl sample -uniform float uThreshold< - string label = "Edge Threshold"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 2; // max based on the webgl sample, which is 2 - float step = 0.01; -> = 0.190; // the default value is based on the webgl sample - -// smartDeNoise - parameters -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// float2 uv - actual fragment coord -// float2 size - window size -// float sigma > 0 - sigma Standard Deviation -// float kSigma >= 0 - sigma coefficient -// kSigma * sigma --> radius of the circular kernel -// float threshold - edge sharpening threshold -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default -float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) -{ - float radius = round(kSigma * sigma); - float radQ = radius * radius; - float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) - float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) - - float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) - float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) - - float4 centrPx = image.Sample(textureSampler, uv); - float zBuff = 0.0; - float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float2 d; - for (d.x = -radius; d.x <= radius; d.x += 1.0) - { - float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend - d.y = -pt; - for (; d.y <= pt; d.y += 1.0) - { - float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; - float4 walkPx = image.Sample(textureSampler, uv + (d / size)); - float4 dC = walkPx - centrPx; - float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; - zBuff += deltaFactor; - aBuff += (walkPx * deltaFactor); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - return aBuff / float4(zBuff, zBuff, zBuff, zBuff); -} - -float4 mainImage(VertData v_in) : TARGET -{ - return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -55253,270 +58721,219 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecularShineShader { +function Send-OBSSleep { -[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] +[Alias('obs.powershell.websocket.Sleep')] param( -# Set the Hint of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('Hint')] -[String] -$Hint, -# Set the roughness of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('roughness')] -[Single] -$Roughness, -# Set the lightStrength of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('lightStrength')] -[Single] -$LightStrength, -# Set the LightPositionX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionX')] -[Single] -$LightPositionX, -# Set the LightPositionY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionY')] -[Single] -$LightPositionY, -# Set the flattenNormal of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('flattenNormal')] -[Single] -$FlattenNormal, -# Set the stretchNormalX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalX')] -[Single] -$StretchNormalX, -# Set the stretchNormalY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalY')] -[Single] -$StretchNormalY, -# Set the Light_Color of OBSSpecularShineShader -[Alias('Light_Color')] -[ComponentModel.DefaultBindingProperty('Light_Color')] -[Single[]] -$LightColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sleepMillis')] +[ValidateRange(0,50000)] +[double] +$SleepMillis, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sleepFrames')] +[ValidateRange(0,10000)] +[double] +$SleepFrames, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'specular-shine' -$ShaderNoun = 'OBSSpecularShineShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft - -uniform string Hint< - string widget_type = "info"; -> = "Try using a black color source with the additive blend mode!"; - -uniform float roughness< - string label = "Roughness"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.25; - -uniform float lightStrength< - string label = "lightStrength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; - -uniform float LightPositionX< - string label = "Light Position X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float LightPositionY< - string label = "Light Position Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float flattenNormal< - string label = "Flatten Normal"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float stretchNormalX< - string label = "Stretch Normal X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float stretchNormalY< - string label = "Stretch Normal Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float3 Light_Color = {1,1,1}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float Square(float a) { return a * a; } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float CookTorrance(float3 lightDir, float3 normal, float roughness) { - float3 h = normalize(lightDir + float3(0,0,1)); - float nh2 = Square(saturate(dot(normal, h))); - float lh2 = Square(saturate(dot(lightDir, h))); - float r2 = Square(roughness); - float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); - float normalization = roughness * 4.0 + 2.0; - return r2 / (d2 * max(0.1, lh2) * normalization); } -#define PI 3.14159265 -float4 mainImage(VertData v_in) : TARGET -{ +} - float4 c0 = image.Sample(textureSampler, v_in.uv); + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSStreamCaption { - float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); - float2 normalUV = v_in.uv - 0.5; - normalUV.x /= stretchNormalX; - normalUV.y /= stretchNormalY; - normalUV += 0.5; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] +[Alias('obs.powershell.websocket.SendStreamCaption')] +param( - float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); - normal.z *= -1; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('captionText')] +[string] +$CaptionText, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - normal = lerp(normal, float3(0,0,-1), flattenNormal); - float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; +process { - return float4(c0 + light,c0.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -55525,219 +58942,242 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpotlightShader { +function Send-OBSTriggerHotkeyByKeySequence { -[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] +[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] param( -# Set the Speed_Percent of OBSSpotlightShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Single] -$SpeedPercent, -# Set the Focus_Percent of OBSSpotlightShader -[Alias('Focus_Percent')] -[ComponentModel.DefaultBindingProperty('Focus_Percent')] -[Single] -$FocusPercent, -# Set the Glitch of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Spotlight_Color of OBSSpotlightShader -[Alias('Spotlight_Color')] -[ComponentModel.DefaultBindingProperty('Spotlight_Color')] -[String] -$SpotlightColor, -# Set the Horizontal_Offset of OBSSpotlightShader -[Alias('Horizontal_Offset')] -[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] -[Single] -$HorizontalOffset, -# Set the Vertical_Offset of OBSSpotlightShader -[Alias('Vertical_Offset')] -[ComponentModel.DefaultBindingProperty('Vertical_Offset')] -[Single] -$VerticalOffset, -# Set the Notes of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('keyId')] +[string] +$KeyId, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('keyModifiers')] +[PSObject] +$KeyModifiers, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] +[switch] +$KeyModifiersshift, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.control')] +[switch] +$KeyModifierscontrol, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] +[switch] +$KeyModifiersalt, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.command')] +[switch] +$KeyModifierscommand, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'spotlight' -$ShaderNoun = 'OBSSpotlightShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -uniform float Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform float Focus_Percent< - string label = "Focus Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 15.0; -uniform bool Glitch; -uniform float4 Spotlight_Color; -uniform float Horizontal_Offset< - string label = "Horizontal Offset"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Vertical_Offset< - string label = "Vertical Offset"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.5; -uniform string Notes< - string widget_type = "info"; -> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; -float4 mainImage(VertData v_in) : TARGET -{ - float speed = Speed_Percent * 0.01; - float focus = Focus_Percent; - if (Glitch) - { - speed *= ((rand_f * 2) - 1) * 0.01; - focus *= ((rand_f * 1.1) - 0.1); - } - float PI = 3.1415926535897932384626433832795;//acos(-1); - float4 c0 = image.Sample( textureSampler, v_in.uv); - float3 lightsrc = float3(sin(elapsed_time * speed * PI * 0.667) *.5 + .5 + Horizontal_Offset, cos(elapsed_time * speed * PI) *.5 + .5 + Vertical_Offset, 1); - float3 light = normalize(lightsrc - float3( v_in.uv.x + (Horizontal_Offset * speed), v_in.uv.y + (Vertical_Offset * speed), 0)); - c0 *= pow(dot(light, float3(0, 0, 1)), focus) * Spotlight_Color; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - return c0; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSTriggerHotkeyByName { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] +[Alias('obs.powershell.websocket.TriggerHotkeyByName')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('hotkeyName')] +[string] +$HotkeyName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('contextName')] +[string] +$ContextName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -55746,238 +59186,329 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSwirlShader { +function Send-OBSTriggerMediaInputAction { -[Alias('Set-OBSSwirlShader','Add-OBSSwirlShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] +[Alias('obs.powershell.websocket.TriggerMediaInputAction')] param( -# Set the radius of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the angle of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the center_x of OBSSwirlShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSSwirlShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the animate of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the inverse of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('inverse')] -[Management.Automation.SwitchParameter] -$Inverse, -# Set the notes of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaAction')] +[string] +$MediaAction, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Swirl' -$ShaderNoun = 'OBSSwirlShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; // -uniform float angle< - string label = "Angle"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.01; -> = 270.0; // -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // -uniform bool animate = false; -uniform bool inverse = false; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform string notes< - string widget_type = "info"; -> = "Distorts the screen, twisting the image in a circular motion." + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float4 mainImage(VertData v_in) : TARGET -{ - - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float percent = (radius-dist)/(radius); - percent = inverse == false ? percent : 1 - percent; + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv ); - } - } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSTriggerStudioModeTransition { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] +[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentPreviewScene { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -55986,400 +59517,324 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTetraShader { +function Set-OBSCurrentProfile { -[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] +[Alias('obs.powershell.websocket.SetCurrentProfile')] param( -# Set the redR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redR')] -[Single] -$RedR, -# Set the redG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redG')] -[Single] -$RedG, -# Set the redB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redB')] -[Single] -$RedB, -# Set the yelR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelR')] -[Single] -$YelR, -# Set the yelG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelG')] -[Single] -$YelG, -# Set the yelB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelB')] -[Single] -$YelB, -# Set the grnR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnR')] -[Single] -$GrnR, -# Set the grnG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnG')] -[Single] -$GrnG, -# Set the grnB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnB')] -[Single] -$GrnB, -# Set the cynR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynR')] -[Single] -$CynR, -# Set the cynG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynG')] -[Single] -$CynG, -# Set the cynB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynB')] -[Single] -$CynB, -# Set the bluR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluR')] -[Single] -$BluR, -# Set the bluG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluG')] -[Single] -$BluG, -# Set the bluB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluB')] -[Single] -$BluB, -# Set the magR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magR')] -[Single] -$MagR, -# Set the magG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magG')] -[Single] -$MagG, -# Set the magB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magB')] -[Single] -$MagB, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'tetra' -$ShaderNoun = 'OBSTetraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Tetrahedral Interpolation Shader for OBS -uniform float redR< - string label = "Red in Red"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float redG< - string label = "Green in Red"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float redB< - string label = "Blue in Red"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float yelR< - string label = "Red in Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float yelG< - string label = "Green in Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float yelB< - string label = "Blue in Yellow"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform float grnR< - string label = "Red in Green"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +} -uniform float grnG< - string label = "Green in Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float grnB< - string label = "Blue in Green"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +} -uniform float cynR< - string label = "Red in Cyan"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentProgramScene { -uniform float cynG< - string label = "Green in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float cynB< - string label = "Blue in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] +[Alias('obs.powershell.websocket.SetCurrentProgramScene')] +param( -uniform float bluR< - string label = "Red in Blue"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -uniform float bluG< - string label = "Green in Blue"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float bluB< - string label = "Blue in Blue"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform float magR< - string label = "Red in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +} -uniform float magG< - string label = "Green in Magenta"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float magB< - string label = "Blue in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneCollection { -float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { - float r = RGBimage.x; - float g = RGBimage.y; - float b = RGBimage.z; - float3 wht = float3(1.0, 1.0, 1.0); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] +[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] +param( - if (r > g) { - if (g > b) { - // r > g > b - return r * red + g * (yel - red) + b * (wht - yel); - } else if (r > b) { - // r > b > g - return r * red + g * (wht - mag) + b * (mag - red); - } else { - // b > r > g - return r * (mag - blu) + g * (wht - mag) + b * blu; - } - } else { - if (b > g) { - // b > g > r - return r * (wht - cyn) + g * (cyn - blu) + b * blu; - } else if (b > r) { - // g > b > r - return r * (wht - cyn) + g * grn + b * (cyn - grn); - } else { - // g > r > b - return r * (yel - grn) + g * grn + b * (wht - yel); - } - } -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float4 mainImage(VertData v_in) : TARGET -{ - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float alpha = inputColor.a; - float3 red = float3(redR, redG, redB); - float3 yel = float3(yelR, yelG, yelB); - float3 grn = float3(grnR, grnG, grnB); - float3 cyn = float3(cynR, cynG, cynB); - float3 blu = float3(bluR, bluG, bluB); - float3 mag = float3(magR, magG, magB); +process { - float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); - return float4(outputColor, alpha); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -56388,176 +59843,213 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSThermalShader { +function Set-OBSCurrentSceneTransition { -[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] param( -# Set the strength of OBSThermalShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'thermal' -$ShaderNoun = 'OBSThermalShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/mdKXzG -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.1; -> = 100.0; -float greyScale(float3 c) { - return 0.29 * c.r + 0.60 * c.g + 0.11; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float3 heatMap(float greyValue) { - float3 heat; - heat.r = smoothstep(0.5, 0.8, greyValue); - if(greyValue >= 0.8333) { - heat.r *= (1.1 - greyValue) * 5.0; - } - if(greyValue > 0.6) { - heat.g = smoothstep(1.0, 0.7, greyValue); - } else { - heat.g = smoothstep(0.0, 0.7, greyValue); - } - heat.b = smoothstep(1.0, 0.0, greyValue); - if(greyValue <= 0.3333) { - heat.b *= greyValue / 0.3; - } - return heat; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - float greyValue = greyScale(c.rgb); - float3 h = heatMap(greyValue*(strength/100.0)); - return float4(h.r, h.g, h.b, c.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneTransitionDuration { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -56566,219 +60058,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTvCrtSubpixelShader { +function Set-OBSCurrentSceneTransitionSettings { -[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] param( -# Set the channelWidth of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelWidth')] -[Int32] -$ChannelWidth, -# Set the channelHeight of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelHeight')] -[Int32] -$ChannelHeight, -# Set the hGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('hGap')] -[Int32] -$HGap, -# Set the vGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('vGap')] -[Int32] -$VGap, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionSettings')] +[PSObject] +$TransitionSettings, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'tv-crt-subpixel' -$ShaderNoun = 'OBSTvCrtSubpixelShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro - -// width of a single color channel in pixels -uniform int channelWidth< - string label = "Channel Width"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; -// height of color channels in pixels -uniform int channelHeight< - string label = "Channel Height"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 3; - -// horizontal distance between two neighboring pixels -uniform int hGap< - string label = "Horizontal Gap"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; - -// vertical distance between two neighboring pixels -uniform int vGap< - string label = "Vertical Gap"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; - -float4 mainImage(VertData v_in) : TARGET -{ - float columns = float(channelWidth * 3 + hGap); - float pixelHeight = float(channelHeight + vGap); - - float2 fragCoord = v_in.uv * uv_size; - float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); - float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); - float2 sampleUv = pixel / sampleRes; - - // color of sample point - float4 col = image.Sample(textureSampler, sampleUv); - - int column = int(fragCoord.x) % (channelWidth * 3 + hGap); - - // set color based on which channel this fragment corresponds to - if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); - else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); - else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); - else col = float4(0.0, 0.0, 0.0, col.a); - - // offset every other column of pixels - int height = int(pixelHeight); - if (int(pixel.x) % 2 == 0) { - if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); - } else { - if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); - } - // Output to screen - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -56787,202 +60170,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwistShader { +function Set-OBSInputAudioBalance { -[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] +[Alias('obs.powershell.websocket.SetInputAudioBalance')] param( -# Set the center_x_percent of OBSTwistShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSTwistShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the power of OBSTwistShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the rotation of OBSTwistShader -[ComponentModel.DefaultBindingProperty('rotation')] -[Single] -$Rotation, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioBalance')] +[ValidateRange(0,1)] +[double] +$InputAudioBalance, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'twist' -$ShaderNoun = 'OBSTwistShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float power< - string label = "power"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.3; -uniform float rotation< - string label = "rotation"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.001; -> = 2.0; - -#ifndef OPENGL -#define mat2 float2x2 -#endif - -mat2 rotate(float angle){ - return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float d = distance(center_pos,v_in.uv); - if(d > power){ - return image.Sample(textureSampler, v_in.uv); - } - float r = (cos(d*3.14159265359/power) +1)/2 * rotation; - float2 pos = v_in.uv - center_pos; - pos = mul(pos, rotate(r)); - pos += center_pos; - return image.Sample(textureSampler, pos); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -56991,314 +60288,350 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwoPassDropShadowShader { +function Set-OBSInputAudioMonitorType { -[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] param( -# Set the ViewProj of OBSTwoPassDropShadowShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSTwoPassDropShadowShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSTwoPassDropShadowShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSTwoPassDropShadowShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSTwoPassDropShadowShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSTwoPassDropShadowShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSTwoPassDropShadowShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSTwoPassDropShadowShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the shadow_offset_x of OBSTwoPassDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] -[Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSTwoPassDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] -[Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSTwoPassDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] -[Int32] -$ShadowBlurSize, -# Set the shadow_color of OBSTwoPassDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] -[String] -$ShadowColor, -# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader -[Alias('is_alpha_premultiplied')] -[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] -[Management.Automation.SwitchParameter] -$IsAlphaPremultiplied, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorType')] +[string] +$MonitorType, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'two-pass-drop-shadow' -$ShaderNoun = 'OBSTwoPassDropShadowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; } -uniform int shadow_offset_x< - string label = "shadow offset x"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; ->; -uniform int shadow_offset_y< - string label = "shadow offset y"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; -uniform float4 shadow_color; +} -uniform bool is_alpha_premultiplied; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioSyncOffset { -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float sampled_shadow_alpha = 0; - - for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} -float4 mainImage_2_end(VertData v_in) : TARGET -{ - int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, - v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); - - float sampled_shadow_alpha = 0; - - for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) - { - float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] +param( -technique Draw -{ - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } - - pass p1 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage_2_end(v_in); - } -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] +[ValidateRange(-950,20000)] +[double] +$InputAudioSyncOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioTracks { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] +[Alias('obs.powershell.websocket.SetInputAudioTracks')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioTracks')] +[PSObject] +$InputAudioTracks, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -57307,260 +60640,232 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVCRShader { +function Set-OBSInputMute { -[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] +[Alias('obs.powershell.websocket.SetInputMute')] param( -# Set the vertical_shift of OBSVCRShader -[Alias('vertical_shift')] -[ComponentModel.DefaultBindingProperty('vertical_shift')] -[Single] -$VerticalShift, -# Set the distort of OBSVCRShader -[ComponentModel.DefaultBindingProperty('distort')] -[Single] -$Distort, -# Set the vignet of OBSVCRShader -[ComponentModel.DefaultBindingProperty('vignet')] -[Single] -$Vignet, -# Set the stripe of OBSVCRShader -[ComponentModel.DefaultBindingProperty('stripe')] -[Single] -$Stripe, -# Set the vertical_factor of OBSVCRShader -[Alias('vertical_factor')] -[ComponentModel.DefaultBindingProperty('vertical_factor')] -[Single] -$VerticalFactor, -# Set the vertical_height of OBSVCRShader -[Alias('vertical_height')] -[ComponentModel.DefaultBindingProperty('vertical_height')] -[Single] -$VerticalHeight, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputMuted')] +[switch] +$InputMuted, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'VCR' -$ShaderNoun = 'OBSVCRShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldjGzV -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float vertical_shift< - string label = "vertical shift"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 0.4; -uniform float distort< - string label = "distort"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 5.0; - float step = 0.001; -> = 1.2; -uniform float vignet< - string label = "vignet"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float stripe< - string label = "stripe"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_factor< - string label = "vertical factor"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_height< - string label = "vertical height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 30.0; - -float onOff(float a, float b, float c) -{ - return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); -} - -float ramp(float y, float start, float end) -{ - float inside = step(start,y) - step(end,y); - float fact = (y-start)/(end-start)*inside; - return (1.-fact) * inside; - -} - -float modu(float x, float y) -{ - return (x / y) - floor(x / y); -} - -float stripes(float2 uv) -{ - return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; -} -float4 getVideo(float2 uv) -{ - float2 look = uv; - float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); - look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; - float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + - (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); - look.y = modu((look.y + vShift) , 1.); - return image.Sample(textureSampler, look); -} -float2 screenDistort(float2 uv) -{ - uv -= float2(.5,.5); - uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); - uv += float2(.5,.5); - return uv; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv = screenDistort(uv); - float4 video = getVideo(uv); - float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); - float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; - video += stripes(uv); - video *= vignette; - video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; - return float4(video.r, video.g, video.b ,1.0); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputName { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] +[Alias('obs.powershell.websocket.SetInputName')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newInputName')] +[string] +$NewInputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -57569,306 +60874,244 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVHSShader { +function Set-OBSInputSettings { -[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] +[Alias('obs.powershell.websocket.SetInputSettings')] param( -# Set the range of OBSVHSShader -[ComponentModel.DefaultBindingProperty('range')] -[Single] -$Range, -# Set the offsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('offsetIntensity')] -[Single] -$OffsetIntensity, -# Set the noiseQuality of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseQuality')] -[Single] -$NoiseQuality, -# Set the noiseIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseIntensity')] -[Single] -$NoiseIntensity, -# Set the colorOffsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] -[Single] -$ColorOffsetIntensity, -# Set the Alpha_Percentage of OBSVHSShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSVHSShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSVHSShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSVHSShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSVHSShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'VHS' -$ShaderNoun = 'OBSVHSShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 -//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 -//Converted to OpenGL by Exeldro February 19, 2022 -//Use improved input fields by Exeldro April 15, 2023 -uniform float range< - string label = "Wave size (0.05)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.20; - float step = 0.01; -> = 0.05; -uniform float offsetIntensity< - string label = "Offset intensity (0.02)"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 0.20; - float step = 0.01; -> = 0.02; -uniform float noiseQuality< - string label = "Noise number of lines (250)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 10.0; -> = 250.0; -uniform float noiseIntensity< - string label = "Noise intensity (0.88)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.88; -uniform float colorOffsetIntensity< - string label = "Color offset intensity (1.3)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 1.3; -uniform float Alpha_Percentage< - string label = "Aplha percentage (100.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color; -float dot2(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} -float rand(float2 co) -{ - return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float verticalBar(float pos, float uvY, float offset) -{ - float edge0 = (pos - range); - float edge1 = (pos + range); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float x = smoothstep(edge0, pos, uvY) * offset; - x -= smoothstep(pos, edge1, uvY) * offset; - return x; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float modu(float x, float y) -{ - return (x / y) - floor(x / y); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float dot4(float4 a,float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; } -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - for (float i = 0.0; i < 0.71; i += 0.1313) - { - float d = modu(elapsed_time * i, 1.7); - float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); - o *= offsetIntensity; - uv.x += verticalBar(d, uv.y, o); - } - float uvY = uv.y; - uvY *= noiseQuality; - uvY = float(int(uvY)) * (1.0 / noiseQuality); - float noise = rand(float2(elapsed_time * 0.00001, uvY)); - uv.x += noise * noiseIntensity / 100.0; - float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; - float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; +} - float4 rgba = image.Sample(textureSampler, uv); - float r = image.Sample(textureSampler, uv + offsetR).r; - float g = image.Sample(textureSampler, uv + offsetG).g; - float b = rgba.b; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputVolume { - rgba = float4(r, g, b, rgba.a); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); - if (Replace_Image_Color) - color = float4(luma,luma,luma,luma); - rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } - return rgba; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] +[Alias('obs.powershell.websocket.SetInputVolume')] +param( -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeMul')] +[ValidateRange(0,20)] +[double] +$InputVolumeMul, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeDb')] +[ValidateRange(-100,26)] +[double] +$InputVolumeDb, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -57877,201 +61120,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVignettingShader { +function Set-OBSMediaInputCursor { -[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] +[Alias('obs.powershell.websocket.SetMediaInputCursor')] param( -# Set the innerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('innerRadius')] -[Single] -$InnerRadius, -# Set the outerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('outerRadius')] -[Single] -$OuterRadius, -# Set the opacity of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, -# Set the notes of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursor')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$MediaCursor, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'vignetting' -$ShaderNoun = 'OBSVignettingShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -uniform float innerRadius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.9; -uniform float outerRadius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.5; -uniform float opacity< - string label = "opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.8; -uniform string notes< - string widget_type = "info"; -> = "inner radius will always be shown, outer radius is the falloff"; - -float4 mainImage(VertData v_in) : TARGET -{ - float PI = 3.1415926535897932384626433832795;//acos(-1); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; - - float xTrans = (v_in.uv.x * 2) - 1; - float yTrans = 1 - (v_in.uv.y * 2); - - float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); - - float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); - float factor = 1 - subtraction; - - float4 vignetColor = c0 * factor; - vignetColor *= verticalDim; - vignetColor *= opacity; - c0 *= 1-opacity; - - float4 output_color = c0 + vignetColor; - - return float4(output_color); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -58080,230 +61238,227 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVoronoiPixelationShader { +function Set-OBSOutputSettings { -[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] +[Alias('obs.powershell.websocket.SetOutputSettings')] param( -# Set the pixH of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('pixH')] -[Single] -$PixH, -# Set the alternative of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('alternative')] -[Management.Automation.SwitchParameter] -$Alternative, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputSettings')] +[PSObject] +$OutputSettings, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'voronoi-pixelation' -$ShaderNoun = 'OBSVoronoiPixelationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro - -uniform float pixH< - string label = "Size"; - string widget_type = "slider"; - float minimum = 4.0; - float maximum = 500.0; - float step = 0.01; -> = 100.0; -uniform bool alternative; - -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); -} - -float2 random2( float2 p ) { - return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); -} -float2 randomSpin(float2 p, float f){ - return 1.0 * float2( - cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), - sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); -} -float4 VoronoiPixelation(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float2 point1 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point1 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x ; - - return image.Sample(textureSampler, nPoint); -} -float4 VoronoiPixelation2(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); - - - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); - float2 point2 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point2 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - } - float2 nPoint = pixExt + relClos + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x; - - return image.Sample(textureSampler, nPoint); -} - -float4 mainImage(VertData v_in) : TARGET -{ - if (alternative) { - return VoronoiPixelation2(v_in.uv, pixH); - } else { - return VoronoiPixelation(v_in.uv, pixH); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSPersistentData { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] +[Alias('obs.powershell.websocket.SetPersistentData')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotValue')] +[PSObject] +$SlotValue, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -58312,379 +61467,345 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSWalkingDeadPixelFixerShader { +function Set-OBSProfileParameter { -[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] +[Alias('obs.powershell.websocket.SetProfileParameter')] param( -# Set the Scan_Width of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Width')] -[ComponentModel.DefaultBindingProperty('Scan_Width')] -[Int32] -$ScanWidth, -# Set the Scan_Height of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Height')] -[ComponentModel.DefaultBindingProperty('Scan_Height')] -[Int32] -$ScanHeight, -# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Offset_X')] -[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] -[Int32] -$ScanOffsetX, -# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] -[Int32] -$ScanOffsetY, -# Set the Show_Border of OBSWalkingDeadPixelFixerShader -[Alias('Show_Border')] -[ComponentModel.DefaultBindingProperty('Show_Border')] -[Management.Automation.SwitchParameter] -$ShowBorder, -# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader -[Alias('Contrast_Threshold')] -[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] -[Single] -$ContrastThreshold, -# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader -[Alias('Min_Cluster_Size')] -[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] -[Int32] -$MinClusterSize, -# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader -[Alias('Max_Cluster_Size')] -[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] -[Int32] -$MaxClusterSize, -# Set the Show_Green of OBSWalkingDeadPixelFixerShader -[Alias('Show_Green')] -[ComponentModel.DefaultBindingProperty('Show_Green')] -[Management.Automation.SwitchParameter] -$ShowGreen, -# Set the Bypass of OBSWalkingDeadPixelFixerShader -[ComponentModel.DefaultBindingProperty('Bypass')] -[Management.Automation.SwitchParameter] -$Bypass, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterValue')] +[string] +$ParameterValue, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'walking-dead-pixel-fixer' -$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter -// by Eegee http://github.com/eegee/ -// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. -// The pixels located in the user configured scan area and passing the threshold settings will have its colors -// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. -// -// Changelog: -// 0.01 - Initial release -// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. -uniform int Scan_Width< - string label = "Scan area width"; - int minimum = 1; - int maximum = 2560; - int step = 1; -> = 75; -uniform int Scan_Height< - string label = "Scan area height"; - int minimum = 1; - int maximum = 1440; - int step = 1; -> = 120; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int Scan_Offset_X< - string label = "Scan area offset X"; - int minimum = 0; - int maximum = 2560; - int step = 1; -> = 110; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform int Scan_Offset_Y< - string label = "Scan area offset Y"; - int minimum = 0; - int maximum = 1440; - int step = 1; -> = 20; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform bool Show_Border< - string label = "Show scan area border in red"; - string widget_type = "checkbox"; -> = true; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float Contrast_Threshold< - string label = "Contrast threshold"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform int Min_Cluster_Size< - string label = "Min cluster size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 600; - int step = 2; -> = 324; +} -uniform int Max_Cluster_Size< - string label = "Max cluster size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 600; - int step = 2; -> = 400; -uniform bool Show_Green< - string label = "Show matches in green"; - string widget_type = "checkbox"; -> = true; +} -uniform bool Bypass< - string label = "Bypass"; - string widget_type = "checkbox"; -> = false; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSRecordDirectory { -#define SAMPLE_RADIUS 9 +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] +[Alias('obs.powershell.websocket.SetRecordDirectory')] +param( -float luminance(float3 color) -{ - return dot(color, float3(0.299, 0.587, 0.114)); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('recordDirectory')] +[string] +$RecordDirectory, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) -{ - float3 sumColor = float3(0.0, 0.0, 0.0); - float weightSum = 0.0; - contrastCount = 0; - float3 centerColor = image.Sample(textureSampler, uv).rgb; - float centerLum = luminance(centerColor); +process { - for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) - { - for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) - { - if (x == 0 && y == 0) continue; - float2 offset = float2(x, y) / uv_size; - float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - // skip central pixel - if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && - ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) - continue; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; - float lum = luminance(sampleColor); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float weight = 1.0; - sumColor += sampleColor * weight; - weightSum += weight; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if (abs(lum - centerLum) >= contrastThreshold) - contrastCount++; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } - if (weightSum > 0) - { - avgColor = sumColor / weightSum; - } - else - { - avgColor = centerColor; - } - avgLuminance = luminance(avgColor); } -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - float2 pos = v_in.pos.xy; - float4 tex = image.Sample(textureSampler, uv); - float3 color = tex.rgb; +} - if (!Bypass) - { - int pixX = (int)round(pos.x); - int pixY = (int)round(pos.y); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemBlendMode { - int borderwidth = 2; - bool insideScan = - (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && - (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] +param( - bool borderingScan = - (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && - (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - if (insideScan) - { - float3 avgColor; - float avgLum; - int contrastCount; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, - if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) - { - color = avgColor; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] +[string] +$SceneItemBlendMode, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (Show_Green) - { - int left = pixX - borderwidth; - int right = pixX + borderwidth; - int top = pixY - borderwidth; - int bottom = pixY + borderwidth; - bool onOutline = - abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || - abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; +process { - if (onOutline) - return float4(0.0, 1.0, 0.0, 1.0); - } - } - } - else if (Show_Border && borderingScan) - { - return float4(1.0, 0.0, 0.0, 0.5); - } - } - return float4(color, tex.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -58693,283 +61814,245 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZigZagShader { +function Set-OBSSceneItemEnabled { -[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] +[Alias('obs.powershell.websocket.SetSceneItemEnabled')] param( -# Set the radius of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the angle of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the period of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('period')] -[Single] -$Period, -# Set the amplitude of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the center_x of OBSZigZagShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSZigZagShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the phase of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('phase')] -[Single] -$Phase, -# Set the animate of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('animate')] -[Int32] -$Animate, -# Set the notes of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'ZigZag' -$ShaderNoun = 'OBSZigZagShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.0; -uniform float angle< - string label = "angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 180.0; -uniform float period< - string label = "period"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.5; -uniform float amplitude< - string label = "amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; - -uniform float center_x< - string label = "center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; -uniform float center_y< - string label = "center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; - -uniform float phase< - string label = "phase"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform int animate< - string label = "animate"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "No"; - int option_1_value = 1; - string option_1_label = "Amplitude"; - int option_2_value = 2; - string option_2_label = "Time"; -> = 0; - - -uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." - -float4 mainImage(VertData v_in) : TARGET -{ - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; - - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float percent = (radius-dist)/radius; - float theta = percent * percent * - ( - animate == 1 ? - amplitude * sin(elapsed_time) : - amplitude - ) - * sin(percent * percent / period * radians(angle) + (phase + - ( - animate == 2 ? - elapsed_time : - 0 - ))); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv); - } - -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemIndex { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] +[Alias('obs.powershell.websocket.SetSceneItemIndex')] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -58978,256 +62061,244 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomBlurShader { +function Set-OBSSceneItemLocked { -[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] +[Alias('obs.powershell.websocket.SetSceneItemLocked')] param( -# Set the samples of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('samples')] -[Int32] -$Samples, -# Set the magnitude of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('magnitude')] -[Single] -$Magnitude, -# Set the speed_percent of OBSZoomBlurShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the ease of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemLocked')] +[switch] +$SceneItemLocked, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'zoom_blur' -$ShaderNoun = 'OBSZoomBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl -//for Media Player Classic HC or BE -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int samples < - string label = "Samples"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 32; -uniform float magnitude< - string label = "Magnitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform int speed_percent < - string label = "Speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform bool glitch; -uniform string notes< - string widget_type = "info"; -> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float speed = speed_percent * 0.01; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - // circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - b = Styler(t, 0, c, d, ease); - float sample_speed = max(samples * b, 1.0); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float PI = 3.1415926535897932384626433832795;//acos(-1); - float4 c0 = image.Sample(textureSampler, v_in.uv); +} - float xTrans = (v_in.uv.x*2)-1; - float yTrans = 1-(v_in.uv.y*2); - - float angle = atan(yTrans/xTrans) + PI; - if (sign(xTrans) == 1) { - angle+= PI; - } - float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); - float2 currentCoord; - float4 accumulatedColor = float4(0,0,0,0); +} - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor = currentColor; + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemTransform { - accumulatedColor = c0/sample_speed; - for(int i = 1; i< sample_speed; i++) { - float currentRadius ; - // Distance to center dependent - currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); - // Continuous; - // currentRadius = max(0,radius - (0.0004 * i)); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] +[Alias('obs.powershell.websocket.SetSceneItemTransform')] +param( - currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; - currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor += currentColor/sample_speed; - - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - return accumulatedColor; -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemTransform')] +[PSObject] +$SceneItemTransform, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -59236,177 +62307,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomShader { +function Set-OBSSceneName { -[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] +[Alias('obs.powershell.websocket.SetSceneName')] param( -# Set the center_x_percent of OBSZoomShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the power of OBSZoomShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newSceneName')] +[string] +$NewSceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'zoom' -$ShaderNoun = 'OBSZoomShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float power< - string label = "power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -59415,197 +62424,121 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomXYShader { +function Set-OBSSceneSceneTransitionOverride { -[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] param( -# Set the center_x_percent of OBSZoomXYShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomXYShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the x_power of OBSZoomXYShader -[Alias('x_power')] -[ComponentModel.DefaultBindingProperty('x_power')] -[Single] -$XPower, -# Set the y_power of OBSZoomXYShader -[Alias('y_power')] -[ComponentModel.DefaultBindingProperty('y_power')] -[Single] -$YPower, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Zoom_XY' -$ShaderNoun = 'OBSZoomXYShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Zoom XY Shader - -// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ - -// The allow for an independent Horizontal and Vertical Zoom. -uniform int center_x_percent< - string label = "center x percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float x_power< - string label = "x power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; -uniform float y_power< - string label = "y power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -59614,3415 +62547,3116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSAudioOutputSource { - - - #> - [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] - param( - # The name of the audio device. - # This name or device ID of the audio device that should be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','DeviceID')] - [string] - $AudioDevice, +function Set-OBSSourceFilterEnabled { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] +[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] +param( - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterEnabled')] +[switch] +$FilterEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - - } - begin { - # Audio Output sources have an inputKind of 'wasapi_output_capture'. - $inputKind = "wasapi_output_capture" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - - if (-not $myParameters["AudioDevice"]) { - $myParameters["AudioDevice"] = "default" + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # Window capture is a bit of a tricky one. - # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. - # and for that, an input needs to exist. - if (-not $myParameters["Name"]) { +} - - if ($myParameters["AudioDevice"]) { - $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] - } - else { - $Name = $myParameters["Name"] = "AudioOutput" - } - } - +} - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSourceFilterIndex { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] +[Alias('obs.powershell.websocket.SetSourceFilterIndex')] +param( - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = $inputKind - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id - foreach ($deviceInfo in $possibleDevices) { - if ( - ($deviceInfo.itemName -eq $AudioDevice) -or - ($deviceInfo.ItemValue -eq $AudioDevice) -or - ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or - ($deviceInfo.ItemValue -like "*$AudioDevice*") -or - ($deviceInfo.ItemName -like "*$AudioDevice*") - ) { - $myParameterData["device_id"] = $deviceInfo.itemValue - break - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$FilterIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Scene"] + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSBrowserSource { - - - [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] - param( - # The uri or file path to display. - # If the uri points to a local file, this will be preferred - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url', 'Href','Path','FilePath','FullName')] - [uri] - $Uri, - - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # The css style used to render the browser page. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # If set, the browser source will shutdown when it is hidden - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, +} - # If set, the browser source will restart when it is activated. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] - [switch] - $RerouteAudio, +} - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] - [int] - $FramesPerSecond, + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSourceFilterName { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] +[Alias('obs.powershell.websocket.SetSourceFilterName')] +param( - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, - } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - $inputKind = "browser_source" - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newFilterName')] +[string] +$NewFilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } +process { - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } } - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true - } - if ($uri.Scheme -eq 'File') { - if (Test-Path $uri.AbsolutePath) { - $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - else - { - if (Test-Path $uri) { - $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) - $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true - } else { - $myParameterData["url"] = "$uri" - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($uri.Segments) { - $uri.Segments[-1] - } elseif ($uri -match '[\\/]') { - @($uri -split '[\\/]')[-1] - } else { - $uri - } - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 +} - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - } - - } -} +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSColorSource { - - - [Alias('Add-OBSColorSource','Get-OBSColorSource')] - param( - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +function Set-OBSSourceFilterSettings { - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, - [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Color, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] +[Alias('obs.powershell.websocket.SetSourceFilterSettings')] +param( - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - - } - begin { - $inputKind = "color_source_v3" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - $hexChar = [Regex]::new('[0-9a-f]') - $hexColors = @($hexChar.Matches($Color)) - - switch ($hexColors.Length) { - 8 { - #full rgba - $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') - } - 6 { - #rgb only, assume ff for alpha - $alpha = 0xff - $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - } - 4 { - #short rgba - $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') - } - 3 { - #short rgb, assume f for alpha - $alpha = 0xff - $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - } - 0 { - # No color provided, default to transparent black - $alpha = 0 - $red = 0 - $green = 0 - $blue = 0 - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - - $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - $realColor = [uint32]::Parse($hexColor,'HexNumber') - - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "#$hexColor" - } - - $myParameterData = [Ordered]@{color=$realColor} +} - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "color_source_v3" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSStreamServiceSettings { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] +[Alias('obs.powershell.websocket.SetStreamServiceSettings')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceType')] +[string] +$StreamServiceType, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceSettings')] +[PSObject] +$StreamServiceSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - return } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSDisplaySource { - - - [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] - param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("monitor")] - [Alias('MonitorNumber','Display','DisplayNumber')] - [int] - $Monitor = 1, +function Set-OBSStudioModeEnabled { - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("capture_cursor")] - [switch] - $CaptureCursor, - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] +[Alias('obs.powershell.websocket.SetStudioModeEnabled')] +param( - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('studioModeEnabled')] +[switch] +$StudioModeEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + +process { - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # Users like 1 indexed, computers like zero-indexed. - $myParameterData["monitor"] = $Monitor - 1 - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "Display $($Monitor)" + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "monitor_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } +} - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSTBarPosition { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] +[Alias('obs.powershell.websocket.SetTBarPosition')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('position')] +[ValidateRange(0,1)] +[double] +$Position, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('release')] +[switch] +$Release, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - return } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } + + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMarkdownSource { - - - [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] - param( - # The markdown text, or the path to a markdown file - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Markdown, +function Set-OBSVideoSettings { - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] +[Alias('obs.powershell.websocket.SetVideoSettings')] +param( - # The css style used to render the markdown. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('fpsNumerator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsNumerator, - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('fpsDenominator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsDenominator, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseWidth')] +[ValidateRange(1,4096)] +[double] +$BaseWidth, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseHeight')] +[ValidateRange(1,4096)] +[double] +$BaseHeight, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputWidth')] +[ValidateRange(1,4096)] +[double] +$OutputWidth, - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputHeight')] +[ValidateRange(1,4096)] +[double] +$OutputHeight, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - } - begin { - $inputKind = "markdown_source" - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - $IsGet = $MyInvocation.InvocationName -like "Get-*" - $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +process { - if ( - $IsGet -or - ($NoVerb -and -not $NonNameParameters) - ) { - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } } - $markdownAsUri = $null - if ($Markdown -like '*.md') { - $markdownAsUri = $markdown -as [uri] - if ($markdownAsUri.Scheme -eq 'File') { - $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["markdown_source"] = 1 - } - else { - + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - } else { - $myParameterData["text"] = $Markdown - $myParameterData["markdown_source"] = 0 } - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($markdownAsUri.Segments) { - $markdownAsUri.Segments[-1] - } elseif ($markdownAsUri -match '[\\/]') { - @($markdownAsUri -split '[\\/]')[-1] - } elseif ($markdownAsUri) { - $markdownAsUri - } else { - "Markdown" - } - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = "markdown_source" - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSOutput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] +[Alias('obs.powershell.websocket.StartOutput')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - return } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaSource { - - - [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file')] - [string] - $FilePath, + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # If set, the source will close when it is inactive. - # By default, this will be set to true. - # To explicitly set it to false, use -CloseWhenInactive:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("close_when_inactive")] - [switch] - $CloseWhenInactive, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # If set, the source will automatically restart. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("looping")] - [Alias('Looping')] - [switch] - $Loop, +} - # If set, will use hardware decoding, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("hw_decode")] - [Alias('HardwareDecoding','hw_decode')] - [switch] - $UseHardwareDecoding, - # If set, will clear the output on the end of the media. - # If this is set to false, the media will freeze on the last frame. - # This is set to true by default. - # To explicitly set to false, use -ClearMediaEnd:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("clear_on_media_end")] - [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] - [switch] - $ClearOnMediaEnd, +} - # Any FFMpeg demuxer options. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ffmpeg_options")] - [Alias('FFMpegOptions', 'FFMpeg_Options')] - [string] - $FFMpegOption, + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSRecord { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] +[Alias('obs.powershell.websocket.StartRecord')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +process { - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] } - $_ - - } - $InputKind = "ffmpeg_source" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ((Test-Path $FilePath)) { - $FilePathItem = Get-Item -Path $FilePath - $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - +} - if ($myParameters['InputSettings']) { - $keys = - @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { - $myParameters['InputSettings'].Keys - } else { - foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { - $prop.Name - } - }) - foreach ($key in $keys) { - $myParameterData[$key] = $myParameters['InputSettings'].$key - } +} - $myParameterData.remove('inputSettings') - } - if (-not $Name) { - - $Name = $myParameters["Name"] = - if ($FilePathItem.Name) { - $FilePathItem.Name - } else { - "Media" - } - - } +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSReplayBuffer { - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] +[Alias('obs.powershell.websocket.StartReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 +process { - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - # Otherwise, if we had a result - if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } + + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSoundCloudSource { - - - [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] - param( - # The uri to display. This must point to a SoundCloud URL. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url','SoundCloudUri','SoundCloudUrl')] - [uri] - $Uri, +function Start-OBSStream { - # If set, will not autoplay. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoAutoPlay, - # If set, will not display album artwork. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoArtwork, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] +[Alias('obs.powershell.websocket.StartStream')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, will not display play count. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoPlayCount, - # If set, will not display uploader info. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoUploaderInfo, +process { - # If provided, will start playing at a given track number. - [Parameter(ValueFromPipelineByPropertyName)] - [int] - $TrackNumber, - # If set, will show a share link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowShare, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # If set, will show a download link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowDownload, + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # If set, will show a buy link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowBuy, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # The color used for the SoundCloud audio bars and buttons. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Color, +} - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, +} - # The css style used to render the browser page. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSVirtualCam { - # If set, the browser source will shutdown when it is hidden - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, - # If set, the browser source will restart when it is activated. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] +[Alias('obs.powershell.websocket.StartVirtualCam')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] - [switch] - $RerouteAudio, - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] - [int] - $FramesPerSecond, +process { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, then "SoundCloud" will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' - - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - if (-not $shouldInclude) { continue nextInputParameter } + } } - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - - } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - # Sound Cloud Sources are really Browser Sources. - $inputKind = "browser_source" + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - - if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { - Write-Error "URI must be from SoundCloud.com" - return + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($uri.Query) { - $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } | - Where-Object { - $_.Settings['LocalFile'] -like '*.SoundCloud.*' - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } +} - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName +} + + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSOutput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] +[Alias('obs.powershell.websocket.StopOutput')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } } - - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - $MyObsPowerShellPath = if ($home) { - Join-Path $home ".obs-powershell" + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ThisSoundCloudSourceFileName = - if ($name) { - "${name}.SoundCloudSource.html" - } else { - "SoundCloudSource.html" - } +} - $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName - - $soundCloudSrc = @( - "https://w.soundcloud.com/player/?url=" - $Uri - "&" - @( - if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} - if ($color) { "color=$color" -replace "\#",'%23'} - if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} - if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } - if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} - if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } - if ($ShowDownload) { "download=true"} else { "download=false" } - if ($ShowBuy) { "buying=true"} else { "buying=false" } - if ($ShowShare) { "sharing=true"} else { "sharing=false" } - ) -join '&' - ) -join '' - - $soundCloudWidget = @( - "" - "" - "" - "" - "" - ) -join ' ' +} - $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force - - $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSRecord { - if (-not $Name) { - $Name = $myParameters['Name'] = 'SoundCloud' - } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] +[Alias('obs.powershell.websocket.StopRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSwitchSource { - - - [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Sources')] - [string[]] - $SourceList, - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." - } - return $true - })] - - [Alias('SelectIndex','SelectName')] - $Select, - # If set, the list of sources will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, +} - # If set, will switch between sources. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch")] - [switch] - $TimeSwitch, + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSReplayBuffer { - # The interval between sources - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_between")] - [timespan] - $Interval, - # The duration between sources that are switching at a time. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_duration")] - [timespan] - $Duration, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] +[Alias('obs.powershell.websocket.StopReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # The item that will be switched in a TimeSwitch, after -Duration and -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $TimeSwitchTo = "Next", - # If set, will switch on the underlying source's media state events. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("media_state_switch")] - [switch] - $MediaStateSwitch, +process { - # The change in media state that should trigger a switch - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] - $MediaStateChange, - # When the source switcher is trigger by media end, this determines the next source that will be switched to. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $MediaSwitchTo = "Next", + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # The name of the transition between sources. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $TransitionName, - # The properties sent to the transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $TransitionProperty, + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - # The name of the transition used to show a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $ShowTransition, - - # The properties sent to the show transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $ShowTransitionProperty, - # The transition used to hide a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $HideTransition, - # The properties sent to the hide transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $HideTransitionProperty, +} - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +} - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSStream { - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] +[Alias('obs.powershell.websocket.StopStream')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] } - $_ - - } - $InputKind = "source_switcher" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSVirtualCam { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] +[Alias('obs.powershell.websocket.StopVirtualCam')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - if ($myParameters[$parameter.Name] -is [timespan]) { - $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - - $selectedIndex = -1 - $sourcesObject = @( - $currentIndex = -1 - foreach ($sourceName in $SourceList) { - $currentIndex++ - $selected = ($null -ne $Select) -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [string] -and - ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) - ) - ) - if ($selected) { - $selectedIndex = $currentIndex - } - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} - } - ) - if ($sourcesObject) { - $myParameterData['sources'] = $sourcesObject - if ($selectedIndex -gt 0) { - $myParameterData["current_index"] = $selectedIndex + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - elseif ($Select -is [int]) { - $myParameterData['current_index'] = $Select - } - - - if ($TransitionName) { - if ($TransitionName -notlike '*_transition') { - $TransitionName = "${TransitionName}_transition" + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - $myParameterData["transition"] = $TransitionName } - if ($TransitionProperty) { - $myParameterData["transition_properties"] = $TransitionProperty + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($ShowTransition) { - if ($ShowTransition -notlike '*_transition') { - $ShowTransition = "${ShowTransition}_transition" - } - $myParameterData["show_transition"] = $ShowTransition + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - if ($ShowTransitionProperty) { - $myParameterData["show_transition_properties"] = $ShowTransitionProperty - } +} - if ($HideTransition) { - if ($HideTransition -notlike '*_transition') { - $HideTransition = "${HideTransition}_transition" - } - $myParameterData["hide_transition"] = $ShowTransition - } - if ($HideTransitionProperty) { - $myParameterData["hide_transition_properties"] = $HideTransitionProperty - } +} - if ($TimeSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["time_switch_to"] = $vvi - break - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSInputMute { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] +[Alias('obs.powershell.websocket.ToggleInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($MediaSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_state_switch_to"] = $vvi - break + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } } - if ($MediaStateChange) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_switch_state"] = $vvi - break + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } } - - if (-not $Name) { - $Name = $myParameters["Name"] = "Source Switcher" - } - - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } +} - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - if ($sceneItem) { - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSOutput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] +[Alias('obs.powershell.websocket.ToggleOutput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - } + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } + } + + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSVLCSource { - - - [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file','Playlist')] - [string[]] - $FilePath, +function Switch-OBSRecord { - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - # If an `[IO.FileInfo]` is provided, this will be the exact file. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] +[Alias('obs.powershell.websocket.ToggleRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." - } - return $true - })] + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - [Alias('SelectIndex','SelectName')] - $Select, + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # If set, will shuffle the playlist - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shuffle")] - [switch] - $Shuffle, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # If set, the playlist will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, +} - # If set, will show subtitles, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle_enable")] - [Alias('ShowSubtitles','Subtitles')] - [switch] - $Subtitle, - # The selected audio track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("track")] - [int] - $AudioTrack, +} - # The selected subtitle track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle")] - [int] - $SubtitleTrack, + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSRecordPause { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] +[Alias('obs.powershell.websocket.ToggleRecordPause')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +process { - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - if (-not $shouldInclude) { continue nextInputParameter } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] } - $_ - - } - $InputKind = "vlc_source" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + continue nextParam + } } - # (either way, if we were called Get- or with no verb, we're done now). - return } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - - $allPaths = @(foreach ($path in $FilePath) { - foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { - $_.Path - } - }) - $playlistObject = @( - $currentIndex = 0 - foreach ($path in $allPaths) { - $currentIndex++ - $selected = $Select -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or - ($select -is [string] -and - ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) - ) - ) - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} - } - ) - $myParameterData['playlist'] = $playlistObject + +} + + +} + - if (-not $Name) { - $Name = $myParameters["Name"] = $FilePathItem.Name - } +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSReplayBuffer { - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] +[Alias('obs.powershell.websocket.ToggleReplayBuffer')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 +process { - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } } + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen - } - - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSWaveformSource { + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] - param( - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # The audio source for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("audio_source")] - [string] - $AudioSource, +} - # The display mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("display_mode")] - [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] - [string] - $DisplayMode, - # The render mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("line","solid","gradient")] - [string] - $RenderMode, +} - # The windowing mode for the waveform. - # This is the mathematical function used to determine the current "window" of audio data. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("hann","hamming","blackman","blackman_harris","none")] - [string] - $WindowMode, + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSStream { - # The color used for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_base")] - [PSObject] - $Color, - # The crest color used for the waveform. - # This will be ignored if the render mode is not "gradient". - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_crest")] - [PSObject] - $CrestColor, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] +[Alias('obs.powershell.websocket.ToggleStream')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # The channel mode for the waveform. - # This can be either mono or stereo. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_mode")] - [ValidateSet("mono","stereo")] - [string] - $ChannelMode, - # The number of pixels between each channel in stereo mode - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_spacing")] - [int] - $ChannelSpacing, +process { + - # If set, will use a radial layout for the waveform - # Radial layouts will ignore the desired height of the source and instead create a square. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("radial_layout")] - [switch] - $RadialLayout, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - # If set, will invert the direction for a radial waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("invert_direction")] - [switch] - $InvertRadialDirection, + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - # If set, will normalize the volume displayed in the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("normalize_volume")] - [switch] - $NoramlizeVolume, + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - # If set, will automatically declare an FFTSize - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("auto_fft_size")] - [switch] - $AutoFftSize, + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - # If set, will attempt to make audio peaks render faster. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fast_peaks")] - [switch] - $FastPeak, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # The width of the waveform bar. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_width")] - [int] - $BarWidth, +} - # The gap between waveform bars. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_gap")] - [int] - $BarGap, - # The width of waveform bar step. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_width")] - [int] - $StepWidth, +} - # The gap between waveform bar steps. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_gap")] - [int] - $StepGap, + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSVirtualCam { - # The low-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_low")] - [int] - $LowCutoff, - # The high-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_high")] - [int] - $HighCutoff, +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] +[Alias('obs.powershell.websocket.ToggleVirtualCam')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - # The floor of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("floor")] - [int] - $Floor, - # The ceiling of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ceiling")] - [int] - $Ceiling, +process { - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("slope")] - [double] - $Slope, - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_q")] - [Alias('RollOffOctaves')] - [double] - $RollOffOctave, + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_rate")] - [double] - $RollOffRate, + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("grad_ratio")] - [double] - $GradientRatio, + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("deadzone")] - [double] - $Deadzone, + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("temporal_smoothing")] - [ValidateSet("none","exp_moving_avg")] - [string] - $TemporalSmoothing, + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +} - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSEffect +{ + + param( + # The name of the effect. [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force + [Alias('EffectName')] + [string] + $Name ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput + + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + } + process { - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } + if (-not $Name) { + $script:OBSFX.Values + } elseif ($script:OBSFX[$name]) { + $script:OBSFX[$name] } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) } - $DynamicParameters +} +#.ExternalHelp obs-powershell-Help.xml +function Import-OBSEffect { - } - begin { - $inputKind = "phandasm_waveform_source" - filter ToOBSColor { - - if ($_ -is [uint32]) { $_ } - elseif ($_ -is [string]) { - if ($_ -match '^\#[a-f0-9]{3,4}$') { - $_ = $_ -replace '[a-f0-9]','$0$0' - } - - if ($_ -match '^#[a-f0-9]{8}$') { - ( - '0x' + - (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') - ) -as [UInt32] - } - elseif ($_ -match '^#[a-f0-9]{6}$') { - - ( - '0xff' + - (($_ -replace '#').ToCharArray()[-1..-6] -join '') - ) -as [UInt32] - } - } - - } + param( + # The source location of the effect. + # This can be a string, file, directory, command, or module. + [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] + [Alias( + 'FromPath', + 'FromModule', + 'FromScript', + 'FromFunction', + 'FullName', + 'Path', + 'Source' + )] + [ValidateScript({ + $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - $MyInvocationName = "$($MyInvocation.InvocationName)" - $myVerb, $myNoun = $MyInvocationName -split '-' - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - $IsGet = $myVerb -eq "Get" - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + return $true + })] + + $From + ) - if ( - $IsGet -or - $NoVerb - ) { - $inputsOfKind = - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - if ($NonNameParameters -and -not $IsGet) { - $paramCopy = [Ordered]@{} + $PSBoundParameters - if ($paramCopy.Name) { $paramCopy.Remove('Name') } - $inputsOfKind | & $myScriptBlock @paramCopy - } else { - $inputsOfKind - } - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + $newEffects = @() + $obsEffectsPattern = [Regex]::new(' + (?> + ^OBS.(?>fx|effects?)\p{P} + | + [\p{P}-[-]]OBS\.(?>fx|effects?)$ + | + \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ + ) + ','IgnoreCase,IgnorePatternWhitespace') + } - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + process { + # Since -From can be many things, but a metric has to be a command, + # the purpose of this function is to essentially resolve many things to a command, + # and then cache that command. - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + # If -From was a string + if ($From -is [string]) { + # It could be a module, so check those first. + :ResolveFromString do { + foreach ($loadedModule in @(Get-Module)) { + # If we find the module, don't try to resolve -From as a path + if ($loadedModule.Name -eq $from) { + # (just set -From again and let the function continue) + $from = $fromModule = $loadedModule;break ResolveFromString + } + } - } + # If we think from was a path + $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) + # attempt to resolve it + if ($resolvedPath) { + $from = Get-Item -LiteralPath $resolvedPath + } + } while ($false) } - - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($AudioSource) { - "$($AudioSource)-Waveform" - } else { - "Waveform" - } + # If -From is a module + if ($from -is [Management.Automation.PSModuleInfo]) { + # recursively call ourselves with all matching commands + @($from.ExportedCommands.Values) -match $obsEffectsPattern | + Import-OBSEffect + # then, make -From a directory + if ($from.Path) { + $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue + } } - if ($myParameterData.color_base) { - $myParameterData.color_base = $myParameterData.color_base | ToOBSColor - } - - if ($myParameterData.color_crest) { - $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor + # If -From is a directory + if ($from -is [IO.DirectoryInfo]) { + $FromDirectory = $from + # recursively call ourselves with all matching scripts + Get-ChildItem -LiteralPath $from.FullName -Recurse -File | + Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | + Import-OBSEffect + return } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + # If -From is a file + if ($from -is [IO.FileInfo]) { + # and it matches the naming convention + if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } + # make -From a command. + $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + # If -From is a command + if ($from -is [Management.Automation.CommandInfo]) { + # decorate the command + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect') } - return - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + if ($from -is [Management.Automation.ApplicationInfo]) { + $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' + $newEffect = [PSCustomObject][Ordered]@{ + PSTypeName = 'OBS.PowerShell.Effect' + Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json + EffectName = $effectName + TypeName = $TypeName } - } + $script:OBSFX[$effectName] = $newEffect + $newEffects += $newEffect + $newEffect + } else { + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') + } + # and add it to our list of new metrics + $newEffects+= $from + $script:OBSFX[$from.EffectName] = $from + $from + } + } + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string] + $EffectName + ) + + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + } + + process { + if ($script:OBSFX[$name]) { + $script:OBSFX.Stop() + $script:OBSFX.Remove($name) } - } } #.ExternalHelp obs-powershell-Help.xml -function Set-OBSWindowSource { +function Start-OBSEffect +{ - - [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] + [CmdletBinding(PositionalBinding=$false)] param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] - [string] - $WindowTitle, + # The name of the effect. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + $effectNames = @(Get-OBSEffect| + Select-Object -Unique -ExpandProperty EffectName) + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($effectNames -replace '^', "'" -replace '$',"'") + } + })] + [Parameter(Mandatory)] + [string[]] + $EffectName, - # The number of the capture method. By default, automatic (0). - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("method")] - [int] - $CaptureMethod, + # The duration of the effect. + # If provided, all effects should use this duration. + # If not provided, each effect should use it's own duration. + [Timespan] + $Duration, - # The capture priority. + # The parameters passed to the effect. [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet('ExactMatch','SameType','SameExecutable')] - [string] - $CapturePriority, + [Alias('EffectParameters')] + [Collections.IDictionary] + $EffectParameter = @{}, - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cursor")] - [switch] - $CaptureCursor, + # The arguments passed to the effect. + [Parameter(ValueFromRemainingArguments)] + [Alias('EffectArguments')] + [PSObject[]] + $EffectArgument = @(), - # If set, will capture the client area. - # This will be set by default. + # If provided, will step thru running [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("client_area")] - [switch] - $ClientArea, + [Alias('ticks')] + [int] + $Step, - # If set, will force SDR. + # The SceneItemID. If this is provided, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("force_sdr")] - [switch] - $ForceSDR, + [int] + $SceneItemID, - # The name of the scene. - # If no scene name is provided, the current program scene will be used. + # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] [string] - $Scene, + $SceneName, - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] [string] - $Name, + $FilterName, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. + # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. [Parameter(ValueFromPipelineByPropertyName)] + [string] + $SourceName, + + # If set, will loop the effect. [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + $Loop, + # If provided, will loop the effect a number of times. + [int] + $LoopCount, - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters + # If set, will bounce the effect (flip it / reverse it) + [switch] + $Bounce, - } - begin { - $InputKind = "window_capture" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + # If set, will reverse an effect. + [switch] + $Reverse + ) - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } + process { + foreach ($NameOfEffect in $EffectName) { + $obsEffect = Get-OBSEffect -EffectName $NameOfEffect - - if (-not $myParameters["WindowTitle"]) { - while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { - $_ = $_.Parent - } - if ($_.MainWindowTitle) { - $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" + if (-not $obsEffect) { + Write-Warning "No Effect named '$NameOfEffect'" + continue } - } - # Window capture is a bit of a tricky one. - # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. - # and for that, an input needs to exist. - if (-not $myParameters["Name"]) { - if ($myParameters["WindowTitle"]) { - $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] + if ($LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force } - else { - $Name = "WindowCapture" + + if ($loop -or $Bounce) { + $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force + if (-not $LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force + } + } else { + $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force } - } - - - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + if ($Reverse) { + $obsEffect.Reversed = $true + } + + if ($obsEffect -isnot [Management.Automation.CommandInfo]) { + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + $obsEffect.Start() + + } else { + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + if (-not $this) { + if ($_.pstypenames -like '*.GetSourceFilter*') { + $this = $_ + } elseif ($FilterName -and $SourceName) { + $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName + } + + if ($_.pstypenames -like '*.GetSceneItem*') { + $this = $_ + } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { + $this = + foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { + if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { + $sceneItem;break + } + elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { + $sceneItem;break + } + } + } + } - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + if ($Duration -and $obsEffect.Parameters.Duration) { + $EffectParameter.Duration = $Duration } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument + if ($obsEffectOutput) { + $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force + if ($step) { + $obsEffect.Step($step) + } else { + $obsEffect.Start() + } } } + $obsEffect } + + + } +} +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [string] + $EffectName) - if ($null -ne $CaptureMethod) { - $myParameterData["method"] = $CaptureMethod - } - if ($CapturePriority -eq 'ExactMatch') { - $myParameterData["priority"] = 1 - } - elseif ($CapturePriority -eq 'SameType') { - $myParameterData["priority"] = 0 - } - elseif ($CapturePriority -eq 'SameExecutable') { - $myParameterData["priority"] = 2 - } - - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "window_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window - foreach ($windowInfo in $possibleWindows) { - if (-not $WindowTitle) { continue } - if ( - ($windowInfo.itemName -eq $WindowTitle) -or - ($windowInfo.ItemValue -eq $WindowTitle) -or - ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or - ($windowInfo.ItemValue -like "*$WindowTitle*") -or - ($windowInfo.ItemName -like "*$WindowTitle*") - ) { - $myParameterData["window"] = $windowInfo.itemValue - break - } - } + process { + $obsEffect = Get-OBSEffect -EffectName $EffectName - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return - } + if (-not $obsEffect) { return } - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name - } - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - } - + $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force } -} +} \ No newline at end of file From 57807c3b69d08644868d7bf98d5d2879db13e6eb Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Fri, 22 May 2026 10:17:34 -0700 Subject: [PATCH 183/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- Commands/Stop-OBS.ps1 | 149 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 Commands/Stop-OBS.ps1 diff --git a/Commands/Stop-OBS.ps1 b/Commands/Stop-OBS.ps1 new file mode 100644 index 00000000..b1711fa4 --- /dev/null +++ b/Commands/Stop-OBS.ps1 @@ -0,0 +1,149 @@ +function Stop-OBS { + <# + .SYNOPSIS + Stops OBS + .DESCRIPTION + Stops OBS. + + By default, stops recording and streaming. + + If -Process is provided, will stop all running OBS processes + + If -Recording is provided, will stop recording + If -Streaming if provided, will stop obs streaming + If -VirtualCamera is provided, will stop the virtual camera + .NOTES + This command Supports Should Process and has a ConfirmImpact of 'High' + + In an interactive session, this command prompt by default. + + If `-WhatIf` is passed, will output what happen if this ran + If `-Confirm:$false`, confirmation will be skipped. + .EXAMPLE + # Stop Streaming and Recording + Stop-OBS + .EXAMPLE + # Stops obs recording + Stop-OBS -Recording + .EXAMPLE + # Stop Streaming + Stop-OBS -Streaming + .EXAMPLE + # Stop OBS Virtual Camera + Stop-OBS -VirtualCamera + .EXAMPLE + # Stop OBS Virtual Camera without prompting + Stop-OBS -VirtualCamera -Confirm:$false + .EXAMPLE + Stop-OBS -StudioMode + .LINK + Stop-OBSRecord + .LINK + Stop-OBSStream + .LINK + Stop-OBSVirtualCam + .LINK + Set-OBSStudioModeEnabled + #> + [CmdletBinding( + ConfirmImpact='High', + PositionalBinding=$false, + SupportsShouldProcess + )] + param( + # If set, will stop recording. + [switch] + $Recording, + + # If set, will stop streaming + [switch] + $Streaming, + + # If set, will stop the virtual camera. + [switch] + $VirtualCamera, + + # If set, will stop the OBS process. + [switch] + $Process, + + # If set, will enable studio mode. + [switch] + $StudioMode + ) + + if ($Process) { + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand('obs', 'Application') + if (-not $obsApp -and -not ($IsMacOS -or $IsLinux)) { + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand( + ( + Join-Path $env:ProgramFiles "obs-studio" | + Join-Path -ChildPath 'bin' | + Join-Path -ChildPath '64bit' | + Join-Path -ChildPath 'obs64.exe' + ), + 'Application' + ) + } + + if (-not $obsApp) { + Write-Error "OBS not found" + return + } + + $obsRunning = Get-Process | + Where-Object Path -EQ $obsApp.Source + + if (-not $obsRunning) { + Write-Error "OBS not running" + return + } + + if ($WhatIfPreference) { return $obsRunning } + if ($PSCmdlet.ShouldProcess('Stop OBS')) { + $obsRunning | Stop-Process -PassThru + } + return + } + + # Without any parameters, we will stop + if (-not $PSBoundParameters.Count) { + # recording + $Recording = $true + # and streaming. + $Streaming = $true + } + + + + # If we want to stop recording, + if ($Recording -and $PSCmdlet.ShouldProcess('Stop Recording')) { + # `Stop-ObsRecord`. + Stop-OBSRecord -PassThru:$WhatIfPreference + } + + # If we want to stop the virtual camera, + if ($VirtualCamera -and $PSCmdlet.ShouldProcess('Stop Virtual Camera')) { + # `Stop-OBSVirtualCamera`. + Stop-OBSVirtualCam -PassThru:$WhatIfPreference + } + + # If we want to stop streaming, + if ($Streaming -and $PSCmdlet.ShouldProcess('Stop Streaming')) { + # `Stop-OBSStream`. + Stop-OBSStream -PassThru:$WhatIfPreference + } + + if ($StudioMode -and $PSCmdlet.ShouldProcess('Stop Studio Mode')) { + Set-OBSStudioModeEnabled -StudioModeEnabled:$false -PassThru:$WhatIfPreference + } + + # If we are shutting down any part of obs + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera) { + # we do not want to stop obs + return + } +} From 1aae014c6d32d63516753dc756bffc1bc79d7c68 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:19:52 +0000 Subject: [PATCH 184/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- en-us/obs-powershell-commands.help.txt | 1324 ++++++++++++------------ 1 file changed, 672 insertions(+), 652 deletions(-) diff --git a/en-us/obs-powershell-commands.help.txt b/en-us/obs-powershell-commands.help.txt index d3a89177..e002e42a 100644 --- a/en-us/obs-powershell-commands.help.txt +++ b/en-us/obs-powershell-commands.help.txt @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 808 commands -(323 functions and 485 aliases) +obs-powershell exports 838 commands +(333 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](docs/obs-powershell-websocket-commands.md). @@ -12,331 +12,341 @@ Functions ========= -|Name |Synopsis | -|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| -|[Add-OBSInput](docs/Add-OBSInput.md) |Add-OBSInput : CreateInput | -|[Add-OBSProfile](docs/Add-OBSProfile.md) |Add-OBSProfile : CreateProfile | -|[Add-OBSScene](docs/Add-OBSScene.md) |Add-OBSScene : CreateScene | -|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) |Add-OBSSceneCollection : CreateSceneCollection | -|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) |Add-OBSSceneItem : CreateSceneItem | -|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) |Add-OBSSourceFilter : CreateSourceFilter | -|[Clear-OBSScene](docs/Clear-OBSScene.md) |Clears a Scene in OBS | -|[Connect-OBS](docs/Connect-OBS.md) |Connects to Open Broadcast Studio | -|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | -|[Disconnect-OBS](docs/Disconnect-OBS.md) |Disconnects OBS | -|[Get-OBS](docs/Get-OBS.md) |Gets OBS | -|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | -|[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | -|[Get-OBSAddShader](docs/Get-OBSAddShader.md) | -|[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | -|[Get-OBSAlphaGamingBentCameraShader](docs/Get-OBSAlphaGamingBentCameraShader.md) | -|[Get-OBSAnimatedPathShader](docs/Get-OBSAnimatedPathShader.md) | -|[Get-OBSAnimatedTextureShader](docs/Get-OBSAnimatedTextureShader.md) | -|[Get-OBSAsciiShader](docs/Get-OBSAsciiShader.md) | -|[Get-OBSAspectRatioShader](docs/Get-OBSAspectRatioShader.md) | -|[Get-OBSBackgroundRemovalShader](docs/Get-OBSBackgroundRemovalShader.md) | -|[Get-OBSBlendOpacityShader](docs/Get-OBSBlendOpacityShader.md) | -|[Get-OBSBlinkShader](docs/Get-OBSBlinkShader.md) | -|[Get-OBSBloomShader](docs/Get-OBSBloomShader.md) | -|[Get-OBSBorderShader](docs/Get-OBSBorderShader.md) | -|[Get-OBSBoxBlurShader](docs/Get-OBSBoxBlurShader.md) | -|[Get-OBSBulgePinchShader](docs/Get-OBSBulgePinchShader.md) | -|[Get-OBSBurnShader](docs/Get-OBSBurnShader.md) | -|[Get-OBSCartoonShader](docs/Get-OBSCartoonShader.md) | -|[Get-OBSCellShadedShader](docs/Get-OBSCellShadedShader.md) | -|[Get-OBSChromaticAberrationShader](docs/Get-OBSChromaticAberrationShader.md) | -|[Get-OBSChromaUVDistortionShader](docs/Get-OBSChromaUVDistortionShader.md) | -|[Get-OBSCircleMaskFilterShader](docs/Get-OBSCircleMaskFilterShader.md) | -|[Get-OBSClockAnalogShader](docs/Get-OBSClockAnalogShader.md) | -|[Get-OBSClockDigitalLedShader](docs/Get-OBSClockDigitalLedShader.md) | -|[Get-OBSClockDigitalNixieShader](docs/Get-OBSClockDigitalNixieShader.md) | -|[Get-OBSColorDepthShader](docs/Get-OBSColorDepthShader.md) | -|[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | -|[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | -|[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | -|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | -|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | -|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | -|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) |Get-OBSCurrentSceneTransitionCursor : GetCurrentSceneTransitionCursor | -|[Get-OBSCurveShader](docs/Get-OBSCurveShader.md) | -|[Get-OBSCutRectPerCornerShader](docs/Get-OBSCutRectPerCornerShader.md) | -|[Get-OBSCylinderShader](docs/Get-OBSCylinderShader.md) | -|[Get-OBSDarkenShader](docs/Get-OBSDarkenShader.md) | -|[Get-OBSDeadPixelFixerShader](docs/Get-OBSDeadPixelFixerShader.md) | -|[Get-OBSDensitySatHueShader](docs/Get-OBSDensitySatHueShader.md) | -|[Get-OBSDiffuseTransitionShader](docs/Get-OBSDiffuseTransitionShader.md) | -|[Get-OBSDigitalRainShader](docs/Get-OBSDigitalRainShader.md) | -|[Get-OBSDivideRotateShader](docs/Get-OBSDivideRotateShader.md) | -|[Get-OBSDoodleShader](docs/Get-OBSDoodleShader.md) | -|[Get-OBSDrawingsShader](docs/Get-OBSDrawingsShader.md) | -|[Get-OBSDropShadowShader](docs/Get-OBSDropShadowShader.md) | -|[Get-OBSDrunkShader](docs/Get-OBSDrunkShader.md) | -|[Get-OBSDynamicMaskShader](docs/Get-OBSDynamicMaskShader.md) | -|[Get-OBSEdgeDetectionShader](docs/Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](docs/Get-OBSEffect.md) |Gets OBS Effects | -|[Get-OBSEmbersShader](docs/Get-OBSEmbersShader.md) | -|[Get-OBSEmbossColorShader](docs/Get-OBSEmbossColorShader.md) | -|[Get-OBSEmbossShader](docs/Get-OBSEmbossShader.md) | -|[Get-OBSExeldroBentCameraShader](docs/Get-OBSExeldroBentCameraShader.md) | -|[Get-OBSFadeTransitionShader](docs/Get-OBSFadeTransitionShader.md) | -|[Get-OBSFillColorGradientShader](docs/Get-OBSFillColorGradientShader.md) | -|[Get-OBSFillColorLinearShader](docs/Get-OBSFillColorLinearShader.md) | -|[Get-OBSFillColorRadialDegreesShader](docs/Get-OBSFillColorRadialDegreesShader.md) | -|[Get-OBSFillColorRadialPercentageShader](docs/Get-OBSFillColorRadialPercentageShader.md) | -|[Get-OBSFilterTemplateShader](docs/Get-OBSFilterTemplateShader.md) | -|[Get-OBSFire3Shader](docs/Get-OBSFire3Shader.md) | -|[Get-OBSFireShader](docs/Get-OBSFireShader.md) | -|[Get-OBSFireworks2Shader](docs/Get-OBSFireworks2Shader.md) | -|[Get-OBSFireworksShader](docs/Get-OBSFireworksShader.md) | -|[Get-OBSFisheyeShader](docs/Get-OBSFisheyeShader.md) | -|[Get-OBSFisheyeXyShader](docs/Get-OBSFisheyeXyShader.md) | -|[Get-OBSFlipShader](docs/Get-OBSFlipShader.md) | -|[Get-OBSFrostedGlassShader](docs/Get-OBSFrostedGlassShader.md) | -|[Get-OBSGammaCorrectionShader](docs/Get-OBSGammaCorrectionShader.md) | -|[Get-OBSGaussianBlurAdvancedShader](docs/Get-OBSGaussianBlurAdvancedShader.md) | -|[Get-OBSGaussianBlurShader](docs/Get-OBSGaussianBlurShader.md) | -|[Get-OBSGaussianBlurSimpleShader](docs/Get-OBSGaussianBlurSimpleShader.md) | -|[Get-OBSGaussianExampleShader](docs/Get-OBSGaussianExampleShader.md) | -|[Get-OBSGaussianSimpleShader](docs/Get-OBSGaussianSimpleShader.md) | -|[Get-OBSGbCameraShader](docs/Get-OBSGbCameraShader.md) | -|[Get-OBSGlassShader](docs/Get-OBSGlassShader.md) | -|[Get-OBSGlitchAnalogShader](docs/Get-OBSGlitchAnalogShader.md) | -|[Get-OBSGlitchShader](docs/Get-OBSGlitchShader.md) | -|[Get-OBSGlowShader](docs/Get-OBSGlowShader.md) | -|[Get-OBSGradientShader](docs/Get-OBSGradientShader.md) | -|[Get-OBSGroup](docs/Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | -|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | -|[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | -|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | -|[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | -|[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](docs/Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | -|[Get-OBSHslHsvSaturationShader](docs/Get-OBSHslHsvSaturationShader.md) | -|[Get-OBSHueRotatonShader](docs/Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](docs/Get-OBSInput.md) |Get-OBSInput : GetInputList | -|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) |Get-OBSInputAudioBalance : GetInputAudioBalance | -|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) |Get-OBSInputAudioMonitorType : GetInputAudioMonitorType | -|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) |Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset | -|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) |Get-OBSInputAudioTracks : GetInputAudioTracks | -|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) |Get-OBSInputDefaultSettings : GetInputDefaultSettings | -|[Get-OBSInputKind](docs/Get-OBSInputKind.md) |Get-OBSInputKind : GetInputKindList | -|[Get-OBSInputMute](docs/Get-OBSInputMute.md) |Get-OBSInputMute : GetInputMute | -|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md)|Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems| -|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) |Get-OBSInputSettings : GetInputSettings | -|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) |Get-OBSInputVolume : GetInputVolume | -|[Get-OBSIntensityScopeShader](docs/Get-OBSIntensityScopeShader.md) | -|[Get-OBSInvertLumaShader](docs/Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) |Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay | -|[Get-OBSLuminance2Shader](docs/Get-OBSLuminance2Shader.md) | -|[Get-OBSLuminanceAlphaShader](docs/Get-OBSLuminanceAlphaShader.md) | -|[Get-OBSLuminanceShader](docs/Get-OBSLuminanceShader.md) | -|[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | -|[Get-OBSMonitor](docs/Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | -|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | -|[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | -|[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | -|[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | -|[Get-OBSOutput](docs/Get-OBSOutput.md) |Get-OBSOutput : GetOutputList | -|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) |Get-OBSOutputSettings : GetOutputSettings | -|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) |Get-OBSOutputStatus : GetOutputStatus | -|[Get-OBSPagePeelShader](docs/Get-OBSPagePeelShader.md) | -|[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | -|[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | -|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | -|[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | -|[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | -|[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | -|[Get-OBSPolarShader](docs/Get-OBSPolarShader.md) | -|[Get-OBSProfile](docs/Get-OBSProfile.md) |Get-OBSProfile : GetProfileList | -|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) |Get-OBSProfileParameter : GetProfileParameter | -|[Get-OBSPulseShader](docs/Get-OBSPulseShader.md) | -|[Get-OBSRainbowShader](docs/Get-OBSRainbowShader.md) | -|[Get-OBSRainWindowShader](docs/Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) |Get-OBSRecordDirectory : GetRecordDirectory | -|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) |Get-OBSRecordStatus : GetRecordStatus | -|[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | -|[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | -|[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | -|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | -|[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | -|[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | -|[Get-OBSRGBAPercentShader](docs/Get-OBSRGBAPercentShader.md) | -|[Get-OBSRgbColorWheelShader](docs/Get-OBSRgbColorWheelShader.md) | -|[Get-OBSRgbSplitShader](docs/Get-OBSRgbSplitShader.md) | -|[Get-OBSRgbvisibilityShader](docs/Get-OBSRgbvisibilityShader.md) | -|[Get-OBSRGSSAAShader](docs/Get-OBSRGSSAAShader.md) | -|[Get-OBSRippleShader](docs/Get-OBSRippleShader.md) | -|[Get-OBSRotatingSourceShader](docs/Get-OBSRotatingSourceShader.md) | -|[Get-OBSRotatoeShader](docs/Get-OBSRotatoeShader.md) | -|[Get-OBSRoundedRect2Shader](docs/Get-OBSRoundedRect2Shader.md) | -|[Get-OBSRoundedRectPerCornerShader](docs/Get-OBSRoundedRectPerCornerShader.md) | -|[Get-OBSRoundedRectPerSideShader](docs/Get-OBSRoundedRectPerSideShader.md) | -|[Get-OBSRoundedRectShader](docs/Get-OBSRoundedRectShader.md) | -|[Get-OBSRoundedStrokeGradientShader](docs/Get-OBSRoundedStrokeGradientShader.md) | -|[Get-OBSRoundedStrokeShader](docs/Get-OBSRoundedStrokeShader.md) | -|[Get-OBSScanLineShader](docs/Get-OBSScanLineShader.md) | -|[Get-OBSScene](docs/Get-OBSScene.md) |Get-OBSScene : GetSceneList | -|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) |Get-OBSSceneCollection : GetSceneCollectionList | -|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) |Get-OBSSceneItem : GetSceneItemList | -|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) |Get-OBSSceneItemBlendMode : GetSceneItemBlendMode | -|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) |Get-OBSSceneItemEnabled : GetSceneItemEnabled | -|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) |Get-OBSSceneItemId : GetSceneItemId | -|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) |Get-OBSSceneItemIndex : GetSceneItemIndex | -|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) |Get-OBSSceneItemLocked : GetSceneItemLocked | -|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) |Get-OBSSceneItemSource : GetSceneItemSource | -|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) |Get-OBSSceneItemTransform : GetSceneItemTransform | -|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) |Get-OBSSceneSceneTransitionOverride : GetSceneSceneTransitionOverride | -|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) |Get-OBSSceneTransition : GetSceneTransitionList | -|[Get-OBSSeascapeShader](docs/Get-OBSSeascapeShader.md) | -|[Get-OBSSeasickShader](docs/Get-OBSSeasickShader.md) | -|[Get-OBSSelectiveColorShader](docs/Get-OBSSelectiveColorShader.md) | -|[Get-OBSShakeShader](docs/Get-OBSShakeShader.md) | -|[Get-OBSShineShader](docs/Get-OBSShineShader.md) | -|[Get-OBSSimpleGradientShader](docs/Get-OBSSimpleGradientShader.md) | -|[Get-OBSSimplexNoiseShader](docs/Get-OBSSimplexNoiseShader.md) | -|[Get-OBSSmartDenoiseShader](docs/Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) |Get-OBSSourceActive : GetSourceActive | -|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) |Get-OBSSourceFilter : GetSourceFilter | -|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) |Get-OBSSourceFilterDefaultSettings : GetSourceFilterDefaultSettings | -|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) |Get-OBSSourceFilterKind : GetSourceFilterKindList | -|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) |Get-OBSSourceFilterList : GetSourceFilterList | -|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) |Get-OBSSourceScreenshot : GetSourceScreenshot | -|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) |Get-OBSSpecialInputs : GetSpecialInputs | -|[Get-OBSSpecularShineShader](docs/Get-OBSSpecularShineShader.md) | -|[Get-OBSSpotlightShader](docs/Get-OBSSpotlightShader.md) | -|[Get-OBSStats](docs/Get-OBSStats.md) |Get-OBSStats : GetStats | -|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) |Get-OBSStreamServiceSettings : GetStreamServiceSettings | -|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) |Get-OBSStreamStatus : GetStreamStatus | -|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) |Get-OBSStudioModeEnabled : GetStudioModeEnabled | -|[Get-OBSSwirlShader](docs/Get-OBSSwirlShader.md) | -|[Get-OBSTetraShader](docs/Get-OBSTetraShader.md) | -|[Get-OBSThermalShader](docs/Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) |Get-OBSTransitionKind : GetTransitionKindList | -|[Get-OBSTvCrtSubpixelShader](docs/Get-OBSTvCrtSubpixelShader.md) | -|[Get-OBSTwistShader](docs/Get-OBSTwistShader.md) | -|[Get-OBSTwoPassDropShadowShader](docs/Get-OBSTwoPassDropShadowShader.md) | -|[Get-OBSVCRShader](docs/Get-OBSVCRShader.md) | -|[Get-OBSVersion](docs/Get-OBSVersion.md) |Get-OBSVersion : GetVersion | -|[Get-OBSVHSShader](docs/Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) |Get-OBSVideoSettings : GetVideoSettings | -|[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | -|[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | -|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | -|[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | -|[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | -|[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | -|[Get-OBSZoomXYShader](docs/Get-OBSZoomXYShader.md) | -|[Hide-OBS](docs/Hide-OBS.md) |Hide OBS | -|[Import-OBSEffect](docs/Import-OBSEffect.md) |Imports Effects | -|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) |Open-OBSInputFiltersDialog : OpenInputFiltersDialog | -|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) |Open-OBSInputInteractDialog : OpenInputInteractDialog | -|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) |Open-OBSInputPropertiesDialog : OpenInputPropertiesDialog | -|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) |Open-OBSSourceProjector : OpenSourceProjector | -|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) |Open-OBSVideoMixProjector : OpenVideoMixProjector | -|[Receive-OBS](docs/Receive-OBS.md) |Receives data from OBS | -|[Remove-OBS](docs/Remove-OBS.md) |Remove OBS | -|[Remove-OBSEffect](docs/Remove-OBSEffect.md) |Removes OBS Effects | -|[Remove-OBSInput](docs/Remove-OBSInput.md) |Remove-OBSInput : RemoveInput | -|[Remove-OBSProfile](docs/Remove-OBSProfile.md) |Remove-OBSProfile : RemoveProfile | -|[Remove-OBSScene](docs/Remove-OBSScene.md) |Remove-OBSScene : RemoveScene | -|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) |Remove-OBSSceneItem : RemoveSceneItem | -|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) |Remove-OBSSourceFilter : RemoveSourceFilter | -|[Resume-OBSRecord](docs/Resume-OBSRecord.md) |Resume-OBSRecord : ResumeRecord | -|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) |Save-OBSReplayBuffer : SaveReplayBuffer | -|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) |Save-OBSSourceScreenshot : SaveSourceScreenshot | -|[Send-OBS](docs/Send-OBS.md) |Sends messages to the OBS websocket. | -|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) |Send-OBSCallVendorRequest : CallVendorRequest | -|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) |Send-OBSCustomEvent : BroadcastCustomEvent | -|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) |Send-OBSOffsetMediaInputCursor : OffsetMediaInputCursor | -|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) |Send-OBSPauseRecord : PauseRecord | -|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) |Send-OBSPressInputPropertiesButton : PressInputPropertiesButton | -|[Send-OBSSleep](docs/Send-OBSSleep.md) |Send-OBSSleep : Sleep | -|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) |Send-OBSStreamCaption : SendStreamCaption | -|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) |Send-OBSTriggerHotkeyByKeySequence : TriggerHotkeyByKeySequence | -|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) |Send-OBSTriggerHotkeyByName : TriggerHotkeyByName | -|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) |Send-OBSTriggerMediaInputAction : TriggerMediaInputAction | -|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) |Send-OBSTriggerStudioModeTransition : TriggerStudioModeTransition | -|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) |Sets an OBS 3D Filter. | -|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) |Adds or sets an audio output source | -|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) |Sets a browser source | -|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) |Sets a color filter | -|[Set-OBSColorSource](docs/Set-OBSColorSource.md) |Adds a color source | -|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) |Set-OBSCurrentPreviewScene : SetCurrentPreviewScene | -|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) |Set-OBSCurrentProfile : SetCurrentProfile | -|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) |Set-OBSCurrentProgramScene : SetCurrentProgramScene | -|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) |Set-OBSCurrentSceneCollection : SetCurrentSceneCollection | -|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) |Set-OBSCurrentSceneTransition : SetCurrentSceneTransition | -|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) |Set-OBSCurrentSceneTransitionDuration : SetCurrentSceneTransitionDuration | -|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) |Set-OBSCurrentSceneTransitionSettings : SetCurrentSceneTransitionSettings | -|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) |Adds a display source | -|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) |Sets a Equalizer filter. | -|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) |Sets a Gain filter. | -|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) |Set-OBSInputAudioBalance : SetInputAudioBalance | -|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) |Set-OBSInputAudioMonitorType : SetInputAudioMonitorType | -|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) |Set-OBSInputAudioSyncOffset : SetInputAudioSyncOffset | -|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) |Set-OBSInputAudioTracks : SetInputAudioTracks | -|[Set-OBSInputMute](docs/Set-OBSInputMute.md) |Set-OBSInputMute : SetInputMute | -|[Set-OBSInputName](docs/Set-OBSInputName.md) |Set-OBSInputName : SetInputName | -|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) |Set-OBSInputSettings : SetInputSettings | -|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) |Set-OBSInputVolume : SetInputVolume | -|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) |Sets a markdown source | -|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) |Set-OBSMediaInputCursor : SetMediaInputCursor | -|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) |Adds a media source | -|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) |Set-OBSOutputSettings : SetOutputSettings | -|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) |Set-OBSPersistentData : SetPersistentData | -|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) |Set-OBSProfileParameter : SetProfileParameter | -|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) |Set-OBSRecordDirectory : SetRecordDirectory | -|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) |Sets a RenderDelay filter. | -|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) |Sets a Scale filter. | -|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) |Set-OBSSceneItemBlendMode : SetSceneItemBlendMode | -|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) |Set-OBSSceneItemEnabled : SetSceneItemEnabled | -|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) |Set-OBSSceneItemIndex : SetSceneItemIndex | -|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) |Set-OBSSceneItemLocked : SetSceneItemLocked | -|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) |Set-OBSSceneItemTransform : SetSceneItemTransform | -|[Set-OBSSceneName](docs/Set-OBSSceneName.md) |Set-OBSSceneName : SetSceneName | -|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) |Set-OBSSceneSceneTransitionOverride : SetSceneSceneTransitionOverride | -|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) |Sets a scroll filter. | -|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) |Sets a Shader filter. | -|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) |Sets a Sharpness filter. | -|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) |Sets a Sound Cloud Source | -|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) |Set-OBSSourceFilterEnabled : SetSourceFilterEnabled | -|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) |Set-OBSSourceFilterIndex : SetSourceFilterIndex | -|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) |Set-OBSSourceFilterName : SetSourceFilterName | -|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) |Set-OBSSourceFilterSettings : SetSourceFilterSettings | -|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) |Set-OBSStreamServiceSettings : SetStreamServiceSettings | -|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) |Set-OBSStudioModeEnabled : SetStudioModeEnabled | -|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) |Adds a VLC playlist source | -|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) |Set-OBSTBarPosition : SetTBarPosition | -|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) |Set-OBSVideoSettings : SetVideoSettings | -|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) |Adds a VLC playlist source | -|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) |OBS Waveform Source | -|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) |Adds or sets a window capture source | -|[Show-OBS](docs/Show-OBS.md) |Shows content in OBS | -|[Start-OBSEffect](docs/Start-OBSEffect.md) |Starts obs-powershell effects. | -|[Start-OBSOutput](docs/Start-OBSOutput.md) |Start-OBSOutput : StartOutput | -|[Start-OBSRecord](docs/Start-OBSRecord.md) |Start-OBSRecord : StartRecord | -|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) |Start-OBSReplayBuffer : StartReplayBuffer | -|[Start-OBSStream](docs/Start-OBSStream.md) |Start-OBSStream : StartStream | -|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) |Start-OBSVirtualCam : StartVirtualCam | -|[Stop-OBSEffect](docs/Stop-OBSEffect.md) |Stops obs-powershell effects. | -|[Stop-OBSOutput](docs/Stop-OBSOutput.md) |Stop-OBSOutput : StopOutput | -|[Stop-OBSRecord](docs/Stop-OBSRecord.md) |Stop-OBSRecord : StopRecord | -|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) |Stop-OBSReplayBuffer : StopReplayBuffer | -|[Stop-OBSStream](docs/Stop-OBSStream.md) |Stop-OBSStream : StopStream | -|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) |Stop-OBSVirtualCam : StopVirtualCam | -|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) |Switch-OBSInputMute : ToggleInputMute | -|[Switch-OBSOutput](docs/Switch-OBSOutput.md) |Switch-OBSOutput : ToggleOutput | -|[Switch-OBSRecord](docs/Switch-OBSRecord.md) |Switch-OBSRecord : ToggleRecord | -|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) |Switch-OBSRecordPause : ToggleRecordPause | -|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) |Switch-OBSReplayBuffer : ToggleReplayBuffer | -|[Switch-OBSStream](docs/Switch-OBSStream.md) |Switch-OBSStream : ToggleStream | -|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) |Switch-OBSVirtualCam : ToggleVirtualCam | -|[Watch-OBS](docs/Watch-OBS.md) |Watches OBS | +|Name |Synopsis| +|------------------------------------------------------------------------------------------------|--------| +|[Add-OBSInput](docs/Add-OBSInput.md) | +|[Add-OBSProfile](docs/Add-OBSProfile.md) | +|[Add-OBSScene](docs/Add-OBSScene.md) | +|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) | +|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) | +|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) | +|[Clear-OBSScene](docs/Clear-OBSScene.md) | +|[Connect-OBS](docs/Connect-OBS.md) | +|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) | +|[Disconnect-OBS](docs/Disconnect-OBS.md) | +|[Get-OBS](docs/Get-OBS.md) | +|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | +|[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | +|[Get-OBSAddShader](docs/Get-OBSAddShader.md) | +|[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | +|[Get-OBSAlphaGamingBentCameraShader](docs/Get-OBSAlphaGamingBentCameraShader.md) | +|[Get-OBSAnimatedPathShader](docs/Get-OBSAnimatedPathShader.md) | +|[Get-OBSAnimatedTextureShader](docs/Get-OBSAnimatedTextureShader.md) | +|[Get-OBSAsciiShader](docs/Get-OBSAsciiShader.md) | +|[Get-OBSAspectRatioShader](docs/Get-OBSAspectRatioShader.md) | +|[Get-OBSAudioShader](docs/Get-OBSAudioShader.md) | +|[Get-OBSBackgroundRemovalShader](docs/Get-OBSBackgroundRemovalShader.md) | +|[Get-OBSBlendOpacityShader](docs/Get-OBSBlendOpacityShader.md) | +|[Get-OBSBlinkShader](docs/Get-OBSBlinkShader.md) | +|[Get-OBSBloomShader](docs/Get-OBSBloomShader.md) | +|[Get-OBSBorderShader](docs/Get-OBSBorderShader.md) | +|[Get-OBSBoxBlurShader](docs/Get-OBSBoxBlurShader.md) | +|[Get-OBSBulgePinchShader](docs/Get-OBSBulgePinchShader.md) | +|[Get-OBSBurnShader](docs/Get-OBSBurnShader.md) | +|[Get-OBSCartoonShader](docs/Get-OBSCartoonShader.md) | +|[Get-OBSCellShadedShader](docs/Get-OBSCellShadedShader.md) | +|[Get-OBSChromaticAberrationShader](docs/Get-OBSChromaticAberrationShader.md) | +|[Get-OBSChromaUVDistortionShader](docs/Get-OBSChromaUVDistortionShader.md) | +|[Get-OBSCircleMaskFilterShader](docs/Get-OBSCircleMaskFilterShader.md) | +|[Get-OBSClockAnalogShader](docs/Get-OBSClockAnalogShader.md) | +|[Get-OBSClockDigitalLedShader](docs/Get-OBSClockDigitalLedShader.md) | +|[Get-OBSClockDigitalNixieShader](docs/Get-OBSClockDigitalNixieShader.md) | +|[Get-OBSColorDepthShader](docs/Get-OBSColorDepthShader.md) | +|[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | +|[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | +|[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | +|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) | +|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) | +|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) | +|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurveShader](docs/Get-OBSCurveShader.md) | +|[Get-OBSCutRectPerCornerShader](docs/Get-OBSCutRectPerCornerShader.md) | +|[Get-OBSCylinderShader](docs/Get-OBSCylinderShader.md) | +|[Get-OBSDarkenShader](docs/Get-OBSDarkenShader.md) | +|[Get-OBSDeadPixelFixerShader](docs/Get-OBSDeadPixelFixerShader.md) | +|[Get-OBSDensitySatHueShader](docs/Get-OBSDensitySatHueShader.md) | +|[Get-OBSDiffuseTransitionShader](docs/Get-OBSDiffuseTransitionShader.md) | +|[Get-OBSDigitalRainShader](docs/Get-OBSDigitalRainShader.md) | +|[Get-OBSDisplacementMapAdvancedInvertShader](docs/Get-OBSDisplacementMapAdvancedInvertShader.md)| +|[Get-OBSDisplacementMapAdvancedShader](docs/Get-OBSDisplacementMapAdvancedShader.md) | +|[Get-OBSDisplacementMapInvertShader](docs/Get-OBSDisplacementMapInvertShader.md) | +|[Get-OBSDisplacementMapShader](docs/Get-OBSDisplacementMapShader.md) | +|[Get-OBSDivideRotateShader](docs/Get-OBSDivideRotateShader.md) | +|[Get-OBSDoodleShader](docs/Get-OBSDoodleShader.md) | +|[Get-OBSDrawingsShader](docs/Get-OBSDrawingsShader.md) | +|[Get-OBSDropShadowShader](docs/Get-OBSDropShadowShader.md) | +|[Get-OBSDrunkShader](docs/Get-OBSDrunkShader.md) | +|[Get-OBSDynamicMaskShader](docs/Get-OBSDynamicMaskShader.md) | +|[Get-OBSEdgeDetectionShader](docs/Get-OBSEdgeDetectionShader.md) | +|[Get-OBSEffect](docs/Get-OBSEffect.md) | +|[Get-OBSEmbersShader](docs/Get-OBSEmbersShader.md) | +|[Get-OBSEmbossColorShader](docs/Get-OBSEmbossColorShader.md) | +|[Get-OBSEmbossShader](docs/Get-OBSEmbossShader.md) | +|[Get-OBSExeldroBentCameraShader](docs/Get-OBSExeldroBentCameraShader.md) | +|[Get-OBSFadeTransitionShader](docs/Get-OBSFadeTransitionShader.md) | +|[Get-OBSFillColorGradientShader](docs/Get-OBSFillColorGradientShader.md) | +|[Get-OBSFillColorLinearShader](docs/Get-OBSFillColorLinearShader.md) | +|[Get-OBSFillColorRadialDegreesShader](docs/Get-OBSFillColorRadialDegreesShader.md) | +|[Get-OBSFillColorRadialPercentageShader](docs/Get-OBSFillColorRadialPercentageShader.md) | +|[Get-OBSFilterTemplateShader](docs/Get-OBSFilterTemplateShader.md) | +|[Get-OBSFire3Shader](docs/Get-OBSFire3Shader.md) | +|[Get-OBSFireShader](docs/Get-OBSFireShader.md) | +|[Get-OBSFireworks2Shader](docs/Get-OBSFireworks2Shader.md) | +|[Get-OBSFireworksShader](docs/Get-OBSFireworksShader.md) | +|[Get-OBSFisheyeShader](docs/Get-OBSFisheyeShader.md) | +|[Get-OBSFisheyeXyShader](docs/Get-OBSFisheyeXyShader.md) | +|[Get-OBSFlipShader](docs/Get-OBSFlipShader.md) | +|[Get-OBSFrostedGlassShader](docs/Get-OBSFrostedGlassShader.md) | +|[Get-OBSGammaCorrectionShader](docs/Get-OBSGammaCorrectionShader.md) | +|[Get-OBSGaussianBlurAdvancedShader](docs/Get-OBSGaussianBlurAdvancedShader.md) | +|[Get-OBSGaussianBlurShader](docs/Get-OBSGaussianBlurShader.md) | +|[Get-OBSGaussianBlurSimpleShader](docs/Get-OBSGaussianBlurSimpleShader.md) | +|[Get-OBSGaussianExampleShader](docs/Get-OBSGaussianExampleShader.md) | +|[Get-OBSGaussianSimpleShader](docs/Get-OBSGaussianSimpleShader.md) | +|[Get-OBSGbCameraShader](docs/Get-OBSGbCameraShader.md) | +|[Get-OBSGlassShader](docs/Get-OBSGlassShader.md) | +|[Get-OBSGlitchAnalogShader](docs/Get-OBSGlitchAnalogShader.md) | +|[Get-OBSGlitchPeriodicShader](docs/Get-OBSGlitchPeriodicShader.md) | +|[Get-OBSGlitchShader](docs/Get-OBSGlitchShader.md) | +|[Get-OBSGlowShader](docs/Get-OBSGlowShader.md) | +|[Get-OBSGradientShader](docs/Get-OBSGradientShader.md) | +|[Get-OBSGroup](docs/Get-OBSGroup.md) | +|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) | +|[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | +|[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | +|[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | +|[Get-OBSHotkey](docs/Get-OBSHotkey.md) | +|[Get-OBSHslHsvSaturationShader](docs/Get-OBSHslHsvSaturationShader.md) | +|[Get-OBSHueRotatonShader](docs/Get-OBSHueRotatonShader.md) | +|[Get-OBSInput](docs/Get-OBSInput.md) | +|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) | +|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) | +|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) | +|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) | +|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) | +|[Get-OBSInputKind](docs/Get-OBSInputKind.md) | +|[Get-OBSInputMute](docs/Get-OBSInputMute.md) | +|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md) | +|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) | +|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) | +|[Get-OBSIntensityScopeShader](docs/Get-OBSIntensityScopeShader.md) | +|[Get-OBSInvertLumaShader](docs/Get-OBSInvertLumaShader.md) | +|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLuminance2Shader](docs/Get-OBSLuminance2Shader.md) | +|[Get-OBSLuminanceAlphaShader](docs/Get-OBSLuminanceAlphaShader.md) | +|[Get-OBSLuminanceShader](docs/Get-OBSLuminanceShader.md) | +|[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | +|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) | +|[Get-OBSMonitor](docs/Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | +|[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | +|[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | +|[Get-OBSNoiseShader](docs/Get-OBSNoiseShader.md) | +|[Get-OBSNormalMapShader](docs/Get-OBSNormalMapShader.md) | +|[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | +|[Get-OBSOutput](docs/Get-OBSOutput.md) | +|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) | +|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) | +|[Get-OBSPagePeelShader](docs/Get-OBSPagePeelShader.md) | +|[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | +|[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | +|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | +|[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | +|[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | +|[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | +|[Get-OBSPolarShader](docs/Get-OBSPolarShader.md) | +|[Get-OBSProfile](docs/Get-OBSProfile.md) | +|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) | +|[Get-OBSPulseShader](docs/Get-OBSPulseShader.md) | +|[Get-OBSQuadrilateralCropShader](docs/Get-OBSQuadrilateralCropShader.md) | +|[Get-OBSRainbowShader](docs/Get-OBSRainbowShader.md) | +|[Get-OBSRainWindowShader](docs/Get-OBSRainWindowShader.md) | +|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) | +|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) | +|[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | +|[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | +|[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | +|[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | +|[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | +|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) | +|[Get-OBSRGBAPercentShader](docs/Get-OBSRGBAPercentShader.md) | +|[Get-OBSRgbColorWheelShader](docs/Get-OBSRgbColorWheelShader.md) | +|[Get-OBSRgbSplitShader](docs/Get-OBSRgbSplitShader.md) | +|[Get-OBSRgbvisibilityShader](docs/Get-OBSRgbvisibilityShader.md) | +|[Get-OBSRGSSAAShader](docs/Get-OBSRGSSAAShader.md) | +|[Get-OBSRippleShader](docs/Get-OBSRippleShader.md) | +|[Get-OBSRotatingSourceShader](docs/Get-OBSRotatingSourceShader.md) | +|[Get-OBSRotatoeShader](docs/Get-OBSRotatoeShader.md) | +|[Get-OBSRoundedRect2Shader](docs/Get-OBSRoundedRect2Shader.md) | +|[Get-OBSRoundedRectPerCornerShader](docs/Get-OBSRoundedRectPerCornerShader.md) | +|[Get-OBSRoundedRectPerSideShader](docs/Get-OBSRoundedRectPerSideShader.md) | +|[Get-OBSRoundedRectShader](docs/Get-OBSRoundedRectShader.md) | +|[Get-OBSRoundedStrokeGradientShader](docs/Get-OBSRoundedStrokeGradientShader.md) | +|[Get-OBSRoundedStrokeShader](docs/Get-OBSRoundedStrokeShader.md) | +|[Get-OBSScanLineShader](docs/Get-OBSScanLineShader.md) | +|[Get-OBSScene](docs/Get-OBSScene.md) | +|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) | +|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) | +|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) | +|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) | +|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) | +|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) | +|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) | +|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) | +|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) | +|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) | +|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) | +|[Get-OBSSeascapeShader](docs/Get-OBSSeascapeShader.md) | +|[Get-OBSSeasickShader](docs/Get-OBSSeasickShader.md) | +|[Get-OBSSelectiveColorShader](docs/Get-OBSSelectiveColorShader.md) | +|[Get-OBSShakeShader](docs/Get-OBSShakeShader.md) | +|[Get-OBSShineShader](docs/Get-OBSShineShader.md) | +|[Get-OBSSimpleGradientShader](docs/Get-OBSSimpleGradientShader.md) | +|[Get-OBSSimplexNoiseShader](docs/Get-OBSSimplexNoiseShader.md) | +|[Get-OBSSmartDenoiseShader](docs/Get-OBSSmartDenoiseShader.md) | +|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) | +|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) | +|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) | +|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) | +|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) | +|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) | +|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) | +|[Get-OBSSpecularShineShader](docs/Get-OBSSpecularShineShader.md) | +|[Get-OBSSpotlightShader](docs/Get-OBSSpotlightShader.md) | +|[Get-OBSStats](docs/Get-OBSStats.md) | +|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) | +|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) | +|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) | +|[Get-OBSSwirlShader](docs/Get-OBSSwirlShader.md) | +|[Get-OBSTetraShader](docs/Get-OBSTetraShader.md) | +|[Get-OBSThermalShader](docs/Get-OBSThermalShader.md) | +|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) | +|[Get-OBSTvCrtSubpixelShader](docs/Get-OBSTvCrtSubpixelShader.md) | +|[Get-OBSTwistShader](docs/Get-OBSTwistShader.md) | +|[Get-OBSTwoPassDropShadowShader](docs/Get-OBSTwoPassDropShadowShader.md) | +|[Get-OBSVCRShader](docs/Get-OBSVCRShader.md) | +|[Get-OBSVersion](docs/Get-OBSVersion.md) | +|[Get-OBSVHSShader](docs/Get-OBSVHSShader.md) | +|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) | +|[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | +|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) | +|[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | +|[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | +|[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | +|[Get-OBSZoomBlurTransitionShader](docs/Get-OBSZoomBlurTransitionShader.md) | +|[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | +|[Get-OBSZoomXYShader](docs/Get-OBSZoomXYShader.md) | +|[Hide-OBS](docs/Hide-OBS.md) | +|[Import-OBSEffect](docs/Import-OBSEffect.md) | +|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) | +|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) | +|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) | +|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) | +|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) | +|[Receive-OBS](docs/Receive-OBS.md) | +|[Remove-OBS](docs/Remove-OBS.md) | +|[Remove-OBSEffect](docs/Remove-OBSEffect.md) | +|[Remove-OBSInput](docs/Remove-OBSInput.md) | +|[Remove-OBSProfile](docs/Remove-OBSProfile.md) | +|[Remove-OBSScene](docs/Remove-OBSScene.md) | +|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) | +|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) | +|[Resume-OBSRecord](docs/Resume-OBSRecord.md) | +|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) | +|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) | +|[Send-OBS](docs/Send-OBS.md) | +|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) | +|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) | +|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) | +|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) | +|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) | +|[Send-OBSSleep](docs/Send-OBSSleep.md) | +|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) | +|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) | +|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) | +|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) | +|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) | +|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) | +|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) | +|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) | +|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) | +|[Set-OBSColorSource](docs/Set-OBSColorSource.md) | +|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) | +|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) | +|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) | +|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) | +|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) | +|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) | +|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) | +|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) | +|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) | +|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) | +|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) | +|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) | +|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) | +|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) | +|[Set-OBSInputMute](docs/Set-OBSInputMute.md) | +|[Set-OBSInputName](docs/Set-OBSInputName.md) | +|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) | +|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) | +|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) | +|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) | +|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) | +|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) | +|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) | +|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) | +|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) | +|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) | +|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) | +|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) | +|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) | +|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) | +|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) | +|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) | +|[Set-OBSSceneName](docs/Set-OBSSceneName.md) | +|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) | +|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) | +|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) | +|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) | +|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) | +|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) | +|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) | +|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) | +|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) | +|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) | +|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) | +|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) | +|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) | +|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) | +|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) | +|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | +|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | +|[Show-OBS](docs/Show-OBS.md) | +|[Start-OBSEffect](docs/Start-OBSEffect.md) | +|[Start-OBSOutput](docs/Start-OBSOutput.md) | +|[Start-OBSRecord](docs/Start-OBSRecord.md) | +|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | +|[Start-OBSStream](docs/Start-OBSStream.md) | +|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | +|[Stop-OBSEffect](docs/Stop-OBSEffect.md) | +|[Stop-OBSOutput](docs/Stop-OBSOutput.md) | +|[Stop-OBSRecord](docs/Stop-OBSRecord.md) | +|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) | +|[Stop-OBSStream](docs/Stop-OBSStream.md) | +|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) | +|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) | +|[Switch-OBSOutput](docs/Switch-OBSOutput.md) | +|[Switch-OBSRecord](docs/Switch-OBSRecord.md) | +|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) | +|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) | +|[Switch-OBSStream](docs/Switch-OBSStream.md) | +|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) | +|[Watch-OBS](docs/Watch-OBS.md) | @@ -344,330 +354,340 @@ Functions Aliases ======= -|Name |ResolvedCommand| -|------------------------------------------------------------------------------------------|---------------| -|[Add-OBSInput](docs/Add-OBSInput.md) | -|[Add-OBSProfile](docs/Add-OBSProfile.md) | -|[Add-OBSScene](docs/Add-OBSScene.md) | -|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) | -|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) | -|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) | -|[Clear-OBSScene](docs/Clear-OBSScene.md) | -|[Connect-OBS](docs/Connect-OBS.md) | -|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) | -|[Disconnect-OBS](docs/Disconnect-OBS.md) | -|[Get-OBS](docs/Get-OBS.md) | -|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | -|[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | -|[Get-OBSAddShader](docs/Get-OBSAddShader.md) | -|[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | -|[Get-OBSAlphaGamingBentCameraShader](docs/Get-OBSAlphaGamingBentCameraShader.md) | -|[Get-OBSAnimatedPathShader](docs/Get-OBSAnimatedPathShader.md) | -|[Get-OBSAnimatedTextureShader](docs/Get-OBSAnimatedTextureShader.md) | -|[Get-OBSAsciiShader](docs/Get-OBSAsciiShader.md) | -|[Get-OBSAspectRatioShader](docs/Get-OBSAspectRatioShader.md) | -|[Get-OBSBackgroundRemovalShader](docs/Get-OBSBackgroundRemovalShader.md) | -|[Get-OBSBlendOpacityShader](docs/Get-OBSBlendOpacityShader.md) | -|[Get-OBSBlinkShader](docs/Get-OBSBlinkShader.md) | -|[Get-OBSBloomShader](docs/Get-OBSBloomShader.md) | -|[Get-OBSBorderShader](docs/Get-OBSBorderShader.md) | -|[Get-OBSBoxBlurShader](docs/Get-OBSBoxBlurShader.md) | -|[Get-OBSBulgePinchShader](docs/Get-OBSBulgePinchShader.md) | -|[Get-OBSBurnShader](docs/Get-OBSBurnShader.md) | -|[Get-OBSCartoonShader](docs/Get-OBSCartoonShader.md) | -|[Get-OBSCellShadedShader](docs/Get-OBSCellShadedShader.md) | -|[Get-OBSChromaticAberrationShader](docs/Get-OBSChromaticAberrationShader.md) | -|[Get-OBSChromaUVDistortionShader](docs/Get-OBSChromaUVDistortionShader.md) | -|[Get-OBSCircleMaskFilterShader](docs/Get-OBSCircleMaskFilterShader.md) | -|[Get-OBSClockAnalogShader](docs/Get-OBSClockAnalogShader.md) | -|[Get-OBSClockDigitalLedShader](docs/Get-OBSClockDigitalLedShader.md) | -|[Get-OBSClockDigitalNixieShader](docs/Get-OBSClockDigitalNixieShader.md) | -|[Get-OBSColorDepthShader](docs/Get-OBSColorDepthShader.md) | -|[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | -|[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | -|[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | -|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) | -|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) | -|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) | -|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) | -|[Get-OBSCurveShader](docs/Get-OBSCurveShader.md) | -|[Get-OBSCutRectPerCornerShader](docs/Get-OBSCutRectPerCornerShader.md) | -|[Get-OBSCylinderShader](docs/Get-OBSCylinderShader.md) | -|[Get-OBSDarkenShader](docs/Get-OBSDarkenShader.md) | -|[Get-OBSDeadPixelFixerShader](docs/Get-OBSDeadPixelFixerShader.md) | -|[Get-OBSDensitySatHueShader](docs/Get-OBSDensitySatHueShader.md) | -|[Get-OBSDiffuseTransitionShader](docs/Get-OBSDiffuseTransitionShader.md) | -|[Get-OBSDigitalRainShader](docs/Get-OBSDigitalRainShader.md) | -|[Get-OBSDivideRotateShader](docs/Get-OBSDivideRotateShader.md) | -|[Get-OBSDoodleShader](docs/Get-OBSDoodleShader.md) | -|[Get-OBSDrawingsShader](docs/Get-OBSDrawingsShader.md) | -|[Get-OBSDropShadowShader](docs/Get-OBSDropShadowShader.md) | -|[Get-OBSDrunkShader](docs/Get-OBSDrunkShader.md) | -|[Get-OBSDynamicMaskShader](docs/Get-OBSDynamicMaskShader.md) | -|[Get-OBSEdgeDetectionShader](docs/Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](docs/Get-OBSEffect.md) | -|[Get-OBSEmbersShader](docs/Get-OBSEmbersShader.md) | -|[Get-OBSEmbossColorShader](docs/Get-OBSEmbossColorShader.md) | -|[Get-OBSEmbossShader](docs/Get-OBSEmbossShader.md) | -|[Get-OBSExeldroBentCameraShader](docs/Get-OBSExeldroBentCameraShader.md) | -|[Get-OBSFadeTransitionShader](docs/Get-OBSFadeTransitionShader.md) | -|[Get-OBSFillColorGradientShader](docs/Get-OBSFillColorGradientShader.md) | -|[Get-OBSFillColorLinearShader](docs/Get-OBSFillColorLinearShader.md) | -|[Get-OBSFillColorRadialDegreesShader](docs/Get-OBSFillColorRadialDegreesShader.md) | -|[Get-OBSFillColorRadialPercentageShader](docs/Get-OBSFillColorRadialPercentageShader.md) | -|[Get-OBSFilterTemplateShader](docs/Get-OBSFilterTemplateShader.md) | -|[Get-OBSFire3Shader](docs/Get-OBSFire3Shader.md) | -|[Get-OBSFireShader](docs/Get-OBSFireShader.md) | -|[Get-OBSFireworks2Shader](docs/Get-OBSFireworks2Shader.md) | -|[Get-OBSFireworksShader](docs/Get-OBSFireworksShader.md) | -|[Get-OBSFisheyeShader](docs/Get-OBSFisheyeShader.md) | -|[Get-OBSFisheyeXyShader](docs/Get-OBSFisheyeXyShader.md) | -|[Get-OBSFlipShader](docs/Get-OBSFlipShader.md) | -|[Get-OBSFrostedGlassShader](docs/Get-OBSFrostedGlassShader.md) | -|[Get-OBSGammaCorrectionShader](docs/Get-OBSGammaCorrectionShader.md) | -|[Get-OBSGaussianBlurAdvancedShader](docs/Get-OBSGaussianBlurAdvancedShader.md) | -|[Get-OBSGaussianBlurShader](docs/Get-OBSGaussianBlurShader.md) | -|[Get-OBSGaussianBlurSimpleShader](docs/Get-OBSGaussianBlurSimpleShader.md) | -|[Get-OBSGaussianExampleShader](docs/Get-OBSGaussianExampleShader.md) | -|[Get-OBSGaussianSimpleShader](docs/Get-OBSGaussianSimpleShader.md) | -|[Get-OBSGbCameraShader](docs/Get-OBSGbCameraShader.md) | -|[Get-OBSGlassShader](docs/Get-OBSGlassShader.md) | -|[Get-OBSGlitchAnalogShader](docs/Get-OBSGlitchAnalogShader.md) | -|[Get-OBSGlitchShader](docs/Get-OBSGlitchShader.md) | -|[Get-OBSGlowShader](docs/Get-OBSGlowShader.md) | -|[Get-OBSGradientShader](docs/Get-OBSGradientShader.md) | -|[Get-OBSGroup](docs/Get-OBSGroup.md) | -|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) | -|[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | -|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | -|[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | -|[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](docs/Get-OBSHotkey.md) | -|[Get-OBSHslHsvSaturationShader](docs/Get-OBSHslHsvSaturationShader.md) | -|[Get-OBSHueRotatonShader](docs/Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](docs/Get-OBSInput.md) | -|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) | -|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) | -|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) | -|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) | -|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) | -|[Get-OBSInputKind](docs/Get-OBSInputKind.md) | -|[Get-OBSInputMute](docs/Get-OBSInputMute.md) | -|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md)| -|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) | -|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) | -|[Get-OBSIntensityScopeShader](docs/Get-OBSIntensityScopeShader.md) | -|[Get-OBSInvertLumaShader](docs/Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) | -|[Get-OBSLuminance2Shader](docs/Get-OBSLuminance2Shader.md) | -|[Get-OBSLuminanceAlphaShader](docs/Get-OBSLuminanceAlphaShader.md) | -|[Get-OBSLuminanceShader](docs/Get-OBSLuminanceShader.md) | -|[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) | -|[Get-OBSMonitor](docs/Get-OBSMonitor.md) | -|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | -|[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | -|[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | -|[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | -|[Get-OBSOutput](docs/Get-OBSOutput.md) | -|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) | -|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) | -|[Get-OBSPagePeelShader](docs/Get-OBSPagePeelShader.md) | -|[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | -|[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) | -|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | -|[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | -|[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | -|[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | -|[Get-OBSPolarShader](docs/Get-OBSPolarShader.md) | -|[Get-OBSProfile](docs/Get-OBSProfile.md) | -|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) | -|[Get-OBSPulseShader](docs/Get-OBSPulseShader.md) | -|[Get-OBSRainbowShader](docs/Get-OBSRainbowShader.md) | -|[Get-OBSRainWindowShader](docs/Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) | -|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) | -|[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | -|[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | -|[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | -|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | -|[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | -|[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) | -|[Get-OBSRGBAPercentShader](docs/Get-OBSRGBAPercentShader.md) | -|[Get-OBSRgbColorWheelShader](docs/Get-OBSRgbColorWheelShader.md) | -|[Get-OBSRgbSplitShader](docs/Get-OBSRgbSplitShader.md) | -|[Get-OBSRgbvisibilityShader](docs/Get-OBSRgbvisibilityShader.md) | -|[Get-OBSRGSSAAShader](docs/Get-OBSRGSSAAShader.md) | -|[Get-OBSRippleShader](docs/Get-OBSRippleShader.md) | -|[Get-OBSRotatingSourceShader](docs/Get-OBSRotatingSourceShader.md) | -|[Get-OBSRotatoeShader](docs/Get-OBSRotatoeShader.md) | -|[Get-OBSRoundedRect2Shader](docs/Get-OBSRoundedRect2Shader.md) | -|[Get-OBSRoundedRectPerCornerShader](docs/Get-OBSRoundedRectPerCornerShader.md) | -|[Get-OBSRoundedRectPerSideShader](docs/Get-OBSRoundedRectPerSideShader.md) | -|[Get-OBSRoundedRectShader](docs/Get-OBSRoundedRectShader.md) | -|[Get-OBSRoundedStrokeGradientShader](docs/Get-OBSRoundedStrokeGradientShader.md) | -|[Get-OBSRoundedStrokeShader](docs/Get-OBSRoundedStrokeShader.md) | -|[Get-OBSScanLineShader](docs/Get-OBSScanLineShader.md) | -|[Get-OBSScene](docs/Get-OBSScene.md) | -|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) | -|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) | -|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) | -|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) | -|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) | -|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) | -|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) | -|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) | -|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) | -|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) | -|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) | -|[Get-OBSSeascapeShader](docs/Get-OBSSeascapeShader.md) | -|[Get-OBSSeasickShader](docs/Get-OBSSeasickShader.md) | -|[Get-OBSSelectiveColorShader](docs/Get-OBSSelectiveColorShader.md) | -|[Get-OBSShakeShader](docs/Get-OBSShakeShader.md) | -|[Get-OBSShineShader](docs/Get-OBSShineShader.md) | -|[Get-OBSSimpleGradientShader](docs/Get-OBSSimpleGradientShader.md) | -|[Get-OBSSimplexNoiseShader](docs/Get-OBSSimplexNoiseShader.md) | -|[Get-OBSSmartDenoiseShader](docs/Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) | -|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) | -|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) | -|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) | -|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) | -|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) | -|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) | -|[Get-OBSSpecularShineShader](docs/Get-OBSSpecularShineShader.md) | -|[Get-OBSSpotlightShader](docs/Get-OBSSpotlightShader.md) | -|[Get-OBSStats](docs/Get-OBSStats.md) | -|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) | -|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) | -|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) | -|[Get-OBSSwirlShader](docs/Get-OBSSwirlShader.md) | -|[Get-OBSTetraShader](docs/Get-OBSTetraShader.md) | -|[Get-OBSThermalShader](docs/Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) | -|[Get-OBSTvCrtSubpixelShader](docs/Get-OBSTvCrtSubpixelShader.md) | -|[Get-OBSTwistShader](docs/Get-OBSTwistShader.md) | -|[Get-OBSTwoPassDropShadowShader](docs/Get-OBSTwoPassDropShadowShader.md) | -|[Get-OBSVCRShader](docs/Get-OBSVCRShader.md) | -|[Get-OBSVersion](docs/Get-OBSVersion.md) | -|[Get-OBSVHSShader](docs/Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) | -|[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) | -|[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | -|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | -|[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | -|[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | -|[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | -|[Get-OBSZoomXYShader](docs/Get-OBSZoomXYShader.md) | -|[Hide-OBS](docs/Hide-OBS.md) | -|[Import-OBSEffect](docs/Import-OBSEffect.md) | -|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) | -|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) | -|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) | -|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) | -|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) | -|[Receive-OBS](docs/Receive-OBS.md) | -|[Remove-OBS](docs/Remove-OBS.md) | -|[Remove-OBSEffect](docs/Remove-OBSEffect.md) | -|[Remove-OBSInput](docs/Remove-OBSInput.md) | -|[Remove-OBSProfile](docs/Remove-OBSProfile.md) | -|[Remove-OBSScene](docs/Remove-OBSScene.md) | -|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) | -|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) | -|[Resume-OBSRecord](docs/Resume-OBSRecord.md) | -|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) | -|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) | -|[Send-OBS](docs/Send-OBS.md) | -|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) | -|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) | -|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) | -|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) | -|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) | -|[Send-OBSSleep](docs/Send-OBSSleep.md) | -|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) | -|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) | -|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) | -|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) | -|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) | -|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) | -|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) | -|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) | -|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) | -|[Set-OBSColorSource](docs/Set-OBSColorSource.md) | -|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) | -|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) | -|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) | -|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) | -|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) | -|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) | -|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) | -|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) | -|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) | -|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) | -|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) | -|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) | -|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) | -|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) | -|[Set-OBSInputMute](docs/Set-OBSInputMute.md) | -|[Set-OBSInputName](docs/Set-OBSInputName.md) | -|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) | -|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) | -|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) | -|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) | -|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) | -|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) | -|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) | -|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) | -|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) | -|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) | -|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) | -|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) | -|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) | -|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) | -|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) | -|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) | -|[Set-OBSSceneName](docs/Set-OBSSceneName.md) | -|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) | -|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) | -|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) | -|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) | -|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) | -|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) | -|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) | -|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) | -|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) | -|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) | -|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) | -|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) | -|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) | -|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) | -|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) | -|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | -|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | -|[Show-OBS](docs/Show-OBS.md) | -|[Start-OBSEffect](docs/Start-OBSEffect.md) | -|[Start-OBSOutput](docs/Start-OBSOutput.md) | -|[Start-OBSRecord](docs/Start-OBSRecord.md) | -|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | -|[Start-OBSStream](docs/Start-OBSStream.md) | -|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | -|[Stop-OBSEffect](docs/Stop-OBSEffect.md) | -|[Stop-OBSOutput](docs/Stop-OBSOutput.md) | -|[Stop-OBSRecord](docs/Stop-OBSRecord.md) | -|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) | -|[Stop-OBSStream](docs/Stop-OBSStream.md) | -|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) | -|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) | -|[Switch-OBSOutput](docs/Switch-OBSOutput.md) | -|[Switch-OBSRecord](docs/Switch-OBSRecord.md) | -|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) | -|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) | -|[Switch-OBSStream](docs/Switch-OBSStream.md) | -|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) | -|[Watch-OBS](docs/Watch-OBS.md) | +|Name |ResolvedCommand| +|------------------------------------------------------------------------------------------------|---------------| +|[Add-OBSInput](docs/Add-OBSInput.md) | +|[Add-OBSProfile](docs/Add-OBSProfile.md) | +|[Add-OBSScene](docs/Add-OBSScene.md) | +|[Add-OBSSceneCollection](docs/Add-OBSSceneCollection.md) | +|[Add-OBSSceneItem](docs/Add-OBSSceneItem.md) | +|[Add-OBSSourceFilter](docs/Add-OBSSourceFilter.md) | +|[Clear-OBSScene](docs/Clear-OBSScene.md) | +|[Connect-OBS](docs/Connect-OBS.md) | +|[Copy-OBSSceneItem](docs/Copy-OBSSceneItem.md) | +|[Disconnect-OBS](docs/Disconnect-OBS.md) | +|[Get-OBS](docs/Get-OBS.md) | +|[Get-OBS3dPanelShader](docs/Get-OBS3dPanelShader.md) | +|[Get-OBS3dSwapTransitionShader](docs/Get-OBS3dSwapTransitionShader.md) | +|[Get-OBSAddShader](docs/Get-OBSAddShader.md) | +|[Get-OBSAlphaBorderShader](docs/Get-OBSAlphaBorderShader.md) | +|[Get-OBSAlphaGamingBentCameraShader](docs/Get-OBSAlphaGamingBentCameraShader.md) | +|[Get-OBSAnimatedPathShader](docs/Get-OBSAnimatedPathShader.md) | +|[Get-OBSAnimatedTextureShader](docs/Get-OBSAnimatedTextureShader.md) | +|[Get-OBSAsciiShader](docs/Get-OBSAsciiShader.md) | +|[Get-OBSAspectRatioShader](docs/Get-OBSAspectRatioShader.md) | +|[Get-OBSAudioShader](docs/Get-OBSAudioShader.md) | +|[Get-OBSBackgroundRemovalShader](docs/Get-OBSBackgroundRemovalShader.md) | +|[Get-OBSBlendOpacityShader](docs/Get-OBSBlendOpacityShader.md) | +|[Get-OBSBlinkShader](docs/Get-OBSBlinkShader.md) | +|[Get-OBSBloomShader](docs/Get-OBSBloomShader.md) | +|[Get-OBSBorderShader](docs/Get-OBSBorderShader.md) | +|[Get-OBSBoxBlurShader](docs/Get-OBSBoxBlurShader.md) | +|[Get-OBSBulgePinchShader](docs/Get-OBSBulgePinchShader.md) | +|[Get-OBSBurnShader](docs/Get-OBSBurnShader.md) | +|[Get-OBSCartoonShader](docs/Get-OBSCartoonShader.md) | +|[Get-OBSCellShadedShader](docs/Get-OBSCellShadedShader.md) | +|[Get-OBSChromaticAberrationShader](docs/Get-OBSChromaticAberrationShader.md) | +|[Get-OBSChromaUVDistortionShader](docs/Get-OBSChromaUVDistortionShader.md) | +|[Get-OBSCircleMaskFilterShader](docs/Get-OBSCircleMaskFilterShader.md) | +|[Get-OBSClockAnalogShader](docs/Get-OBSClockAnalogShader.md) | +|[Get-OBSClockDigitalLedShader](docs/Get-OBSClockDigitalLedShader.md) | +|[Get-OBSClockDigitalNixieShader](docs/Get-OBSClockDigitalNixieShader.md) | +|[Get-OBSColorDepthShader](docs/Get-OBSColorDepthShader.md) | +|[Get-OBSColorGradeFilterShader](docs/Get-OBSColorGradeFilterShader.md) | +|[Get-OBSCornerPinShader](docs/Get-OBSCornerPinShader.md) | +|[Get-OBSCrtCurvatureShader](docs/Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](docs/Get-OBSCubeRotatingShader.md) | +|[Get-OBSCurrentPreviewScene](docs/Get-OBSCurrentPreviewScene.md) | +|[Get-OBSCurrentProgramScene](docs/Get-OBSCurrentProgramScene.md) | +|[Get-OBSCurrentSceneTransition](docs/Get-OBSCurrentSceneTransition.md) | +|[Get-OBSCurrentSceneTransitionCursor](docs/Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurveShader](docs/Get-OBSCurveShader.md) | +|[Get-OBSCutRectPerCornerShader](docs/Get-OBSCutRectPerCornerShader.md) | +|[Get-OBSCylinderShader](docs/Get-OBSCylinderShader.md) | +|[Get-OBSDarkenShader](docs/Get-OBSDarkenShader.md) | +|[Get-OBSDeadPixelFixerShader](docs/Get-OBSDeadPixelFixerShader.md) | +|[Get-OBSDensitySatHueShader](docs/Get-OBSDensitySatHueShader.md) | +|[Get-OBSDiffuseTransitionShader](docs/Get-OBSDiffuseTransitionShader.md) | +|[Get-OBSDigitalRainShader](docs/Get-OBSDigitalRainShader.md) | +|[Get-OBSDisplacementMapAdvancedInvertShader](docs/Get-OBSDisplacementMapAdvancedInvertShader.md)| +|[Get-OBSDisplacementMapAdvancedShader](docs/Get-OBSDisplacementMapAdvancedShader.md) | +|[Get-OBSDisplacementMapInvertShader](docs/Get-OBSDisplacementMapInvertShader.md) | +|[Get-OBSDisplacementMapShader](docs/Get-OBSDisplacementMapShader.md) | +|[Get-OBSDivideRotateShader](docs/Get-OBSDivideRotateShader.md) | +|[Get-OBSDoodleShader](docs/Get-OBSDoodleShader.md) | +|[Get-OBSDrawingsShader](docs/Get-OBSDrawingsShader.md) | +|[Get-OBSDropShadowShader](docs/Get-OBSDropShadowShader.md) | +|[Get-OBSDrunkShader](docs/Get-OBSDrunkShader.md) | +|[Get-OBSDynamicMaskShader](docs/Get-OBSDynamicMaskShader.md) | +|[Get-OBSEdgeDetectionShader](docs/Get-OBSEdgeDetectionShader.md) | +|[Get-OBSEffect](docs/Get-OBSEffect.md) | +|[Get-OBSEmbersShader](docs/Get-OBSEmbersShader.md) | +|[Get-OBSEmbossColorShader](docs/Get-OBSEmbossColorShader.md) | +|[Get-OBSEmbossShader](docs/Get-OBSEmbossShader.md) | +|[Get-OBSExeldroBentCameraShader](docs/Get-OBSExeldroBentCameraShader.md) | +|[Get-OBSFadeTransitionShader](docs/Get-OBSFadeTransitionShader.md) | +|[Get-OBSFillColorGradientShader](docs/Get-OBSFillColorGradientShader.md) | +|[Get-OBSFillColorLinearShader](docs/Get-OBSFillColorLinearShader.md) | +|[Get-OBSFillColorRadialDegreesShader](docs/Get-OBSFillColorRadialDegreesShader.md) | +|[Get-OBSFillColorRadialPercentageShader](docs/Get-OBSFillColorRadialPercentageShader.md) | +|[Get-OBSFilterTemplateShader](docs/Get-OBSFilterTemplateShader.md) | +|[Get-OBSFire3Shader](docs/Get-OBSFire3Shader.md) | +|[Get-OBSFireShader](docs/Get-OBSFireShader.md) | +|[Get-OBSFireworks2Shader](docs/Get-OBSFireworks2Shader.md) | +|[Get-OBSFireworksShader](docs/Get-OBSFireworksShader.md) | +|[Get-OBSFisheyeShader](docs/Get-OBSFisheyeShader.md) | +|[Get-OBSFisheyeXyShader](docs/Get-OBSFisheyeXyShader.md) | +|[Get-OBSFlipShader](docs/Get-OBSFlipShader.md) | +|[Get-OBSFrostedGlassShader](docs/Get-OBSFrostedGlassShader.md) | +|[Get-OBSGammaCorrectionShader](docs/Get-OBSGammaCorrectionShader.md) | +|[Get-OBSGaussianBlurAdvancedShader](docs/Get-OBSGaussianBlurAdvancedShader.md) | +|[Get-OBSGaussianBlurShader](docs/Get-OBSGaussianBlurShader.md) | +|[Get-OBSGaussianBlurSimpleShader](docs/Get-OBSGaussianBlurSimpleShader.md) | +|[Get-OBSGaussianExampleShader](docs/Get-OBSGaussianExampleShader.md) | +|[Get-OBSGaussianSimpleShader](docs/Get-OBSGaussianSimpleShader.md) | +|[Get-OBSGbCameraShader](docs/Get-OBSGbCameraShader.md) | +|[Get-OBSGlassShader](docs/Get-OBSGlassShader.md) | +|[Get-OBSGlitchAnalogShader](docs/Get-OBSGlitchAnalogShader.md) | +|[Get-OBSGlitchPeriodicShader](docs/Get-OBSGlitchPeriodicShader.md) | +|[Get-OBSGlitchShader](docs/Get-OBSGlitchShader.md) | +|[Get-OBSGlowShader](docs/Get-OBSGlowShader.md) | +|[Get-OBSGradientShader](docs/Get-OBSGradientShader.md) | +|[Get-OBSGroup](docs/Get-OBSGroup.md) | +|[Get-OBSGroupSceneItem](docs/Get-OBSGroupSceneItem.md) | +|[Get-OBSHalftoneShader](docs/Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](docs/Get-OBSHardBlinkShader.md) | +|[Get-OBSHeatWaveSimpleShader](docs/Get-OBSHeatWaveSimpleShader.md) | +|[Get-OBSHexagonShader](docs/Get-OBSHexagonShader.md) | +|[Get-OBSHotkey](docs/Get-OBSHotkey.md) | +|[Get-OBSHslHsvSaturationShader](docs/Get-OBSHslHsvSaturationShader.md) | +|[Get-OBSHueRotatonShader](docs/Get-OBSHueRotatonShader.md) | +|[Get-OBSInput](docs/Get-OBSInput.md) | +|[Get-OBSInputAudioBalance](docs/Get-OBSInputAudioBalance.md) | +|[Get-OBSInputAudioMonitorType](docs/Get-OBSInputAudioMonitorType.md) | +|[Get-OBSInputAudioSyncOffset](docs/Get-OBSInputAudioSyncOffset.md) | +|[Get-OBSInputAudioTracks](docs/Get-OBSInputAudioTracks.md) | +|[Get-OBSInputDefaultSettings](docs/Get-OBSInputDefaultSettings.md) | +|[Get-OBSInputKind](docs/Get-OBSInputKind.md) | +|[Get-OBSInputMute](docs/Get-OBSInputMute.md) | +|[Get-OBSInputPropertiesListPropertyItems](docs/Get-OBSInputPropertiesListPropertyItems.md) | +|[Get-OBSInputSettings](docs/Get-OBSInputSettings.md) | +|[Get-OBSInputVolume](docs/Get-OBSInputVolume.md) | +|[Get-OBSIntensityScopeShader](docs/Get-OBSIntensityScopeShader.md) | +|[Get-OBSInvertLumaShader](docs/Get-OBSInvertLumaShader.md) | +|[Get-OBSLastReplayBufferReplay](docs/Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLuminance2Shader](docs/Get-OBSLuminance2Shader.md) | +|[Get-OBSLuminanceAlphaShader](docs/Get-OBSLuminanceAlphaShader.md) | +|[Get-OBSLuminanceShader](docs/Get-OBSLuminanceShader.md) | +|[Get-OBSMatrixShader](docs/Get-OBSMatrixShader.md) | +|[Get-OBSMediaInputStatus](docs/Get-OBSMediaInputStatus.md) | +|[Get-OBSMonitor](docs/Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](docs/Get-OBSMotionBlurShader.md) | +|[Get-OBSMultiplyShader](docs/Get-OBSMultiplyShader.md) | +|[Get-OBSNightSkyShader](docs/Get-OBSNightSkyShader.md) | +|[Get-OBSNoiseShader](docs/Get-OBSNoiseShader.md) | +|[Get-OBSNormalMapShader](docs/Get-OBSNormalMapShader.md) | +|[Get-OBSOpacityShader](docs/Get-OBSOpacityShader.md) | +|[Get-OBSOutput](docs/Get-OBSOutput.md) | +|[Get-OBSOutputSettings](docs/Get-OBSOutputSettings.md) | +|[Get-OBSOutputStatus](docs/Get-OBSOutputStatus.md) | +|[Get-OBSPagePeelShader](docs/Get-OBSPagePeelShader.md) | +|[Get-OBSPagePeelTransitionShader](docs/Get-OBSPagePeelTransitionShader.md) | +|[Get-OBSPerlinNoiseShader](docs/Get-OBSPerlinNoiseShader.md) | +|[Get-OBSPersistentData](docs/Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](docs/Get-OBSPerspectiveShader.md) | +|[Get-OBSPieChartShader](docs/Get-OBSPieChartShader.md) | +|[Get-OBSPixelationShader](docs/Get-OBSPixelationShader.md) | +|[Get-OBSPixelationTransitionShader](docs/Get-OBSPixelationTransitionShader.md) | +|[Get-OBSPolarShader](docs/Get-OBSPolarShader.md) | +|[Get-OBSProfile](docs/Get-OBSProfile.md) | +|[Get-OBSProfileParameter](docs/Get-OBSProfileParameter.md) | +|[Get-OBSPulseShader](docs/Get-OBSPulseShader.md) | +|[Get-OBSQuadrilateralCropShader](docs/Get-OBSQuadrilateralCropShader.md) | +|[Get-OBSRainbowShader](docs/Get-OBSRainbowShader.md) | +|[Get-OBSRainWindowShader](docs/Get-OBSRainWindowShader.md) | +|[Get-OBSRecordDirectory](docs/Get-OBSRecordDirectory.md) | +|[Get-OBSRecordStatus](docs/Get-OBSRecordStatus.md) | +|[Get-OBSRectangularDropShadowShader](docs/Get-OBSRectangularDropShadowShader.md) | +|[Get-OBSReflectShader](docs/Get-OBSReflectShader.md) | +|[Get-OBSRemovePartialPixelsShader](docs/Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](docs/Get-OBSRepeatGridCenterCropShader.md) | +|[Get-OBSRepeatShader](docs/Get-OBSRepeatShader.md) | +|[Get-OBSRepeatTextureShader](docs/Get-OBSRepeatTextureShader.md) | +|[Get-OBSReplayBufferStatus](docs/Get-OBSReplayBufferStatus.md) | +|[Get-OBSRGBAPercentShader](docs/Get-OBSRGBAPercentShader.md) | +|[Get-OBSRgbColorWheelShader](docs/Get-OBSRgbColorWheelShader.md) | +|[Get-OBSRgbSplitShader](docs/Get-OBSRgbSplitShader.md) | +|[Get-OBSRgbvisibilityShader](docs/Get-OBSRgbvisibilityShader.md) | +|[Get-OBSRGSSAAShader](docs/Get-OBSRGSSAAShader.md) | +|[Get-OBSRippleShader](docs/Get-OBSRippleShader.md) | +|[Get-OBSRotatingSourceShader](docs/Get-OBSRotatingSourceShader.md) | +|[Get-OBSRotatoeShader](docs/Get-OBSRotatoeShader.md) | +|[Get-OBSRoundedRect2Shader](docs/Get-OBSRoundedRect2Shader.md) | +|[Get-OBSRoundedRectPerCornerShader](docs/Get-OBSRoundedRectPerCornerShader.md) | +|[Get-OBSRoundedRectPerSideShader](docs/Get-OBSRoundedRectPerSideShader.md) | +|[Get-OBSRoundedRectShader](docs/Get-OBSRoundedRectShader.md) | +|[Get-OBSRoundedStrokeGradientShader](docs/Get-OBSRoundedStrokeGradientShader.md) | +|[Get-OBSRoundedStrokeShader](docs/Get-OBSRoundedStrokeShader.md) | +|[Get-OBSScanLineShader](docs/Get-OBSScanLineShader.md) | +|[Get-OBSScene](docs/Get-OBSScene.md) | +|[Get-OBSSceneCollection](docs/Get-OBSSceneCollection.md) | +|[Get-OBSSceneItem](docs/Get-OBSSceneItem.md) | +|[Get-OBSSceneItemBlendMode](docs/Get-OBSSceneItemBlendMode.md) | +|[Get-OBSSceneItemEnabled](docs/Get-OBSSceneItemEnabled.md) | +|[Get-OBSSceneItemId](docs/Get-OBSSceneItemId.md) | +|[Get-OBSSceneItemIndex](docs/Get-OBSSceneItemIndex.md) | +|[Get-OBSSceneItemLocked](docs/Get-OBSSceneItemLocked.md) | +|[Get-OBSSceneItemSource](docs/Get-OBSSceneItemSource.md) | +|[Get-OBSSceneItemTransform](docs/Get-OBSSceneItemTransform.md) | +|[Get-OBSSceneSceneTransitionOverride](docs/Get-OBSSceneSceneTransitionOverride.md) | +|[Get-OBSSceneTransition](docs/Get-OBSSceneTransition.md) | +|[Get-OBSSeascapeShader](docs/Get-OBSSeascapeShader.md) | +|[Get-OBSSeasickShader](docs/Get-OBSSeasickShader.md) | +|[Get-OBSSelectiveColorShader](docs/Get-OBSSelectiveColorShader.md) | +|[Get-OBSShakeShader](docs/Get-OBSShakeShader.md) | +|[Get-OBSShineShader](docs/Get-OBSShineShader.md) | +|[Get-OBSSimpleGradientShader](docs/Get-OBSSimpleGradientShader.md) | +|[Get-OBSSimplexNoiseShader](docs/Get-OBSSimplexNoiseShader.md) | +|[Get-OBSSmartDenoiseShader](docs/Get-OBSSmartDenoiseShader.md) | +|[Get-OBSSourceActive](docs/Get-OBSSourceActive.md) | +|[Get-OBSSourceFilter](docs/Get-OBSSourceFilter.md) | +|[Get-OBSSourceFilterDefaultSettings](docs/Get-OBSSourceFilterDefaultSettings.md) | +|[Get-OBSSourceFilterKind](docs/Get-OBSSourceFilterKind.md) | +|[Get-OBSSourceFilterList](docs/Get-OBSSourceFilterList.md) | +|[Get-OBSSourceScreenshot](docs/Get-OBSSourceScreenshot.md) | +|[Get-OBSSpecialInputs](docs/Get-OBSSpecialInputs.md) | +|[Get-OBSSpecularShineShader](docs/Get-OBSSpecularShineShader.md) | +|[Get-OBSSpotlightShader](docs/Get-OBSSpotlightShader.md) | +|[Get-OBSStats](docs/Get-OBSStats.md) | +|[Get-OBSStreamServiceSettings](docs/Get-OBSStreamServiceSettings.md) | +|[Get-OBSStreamStatus](docs/Get-OBSStreamStatus.md) | +|[Get-OBSStudioModeEnabled](docs/Get-OBSStudioModeEnabled.md) | +|[Get-OBSSwirlShader](docs/Get-OBSSwirlShader.md) | +|[Get-OBSTetraShader](docs/Get-OBSTetraShader.md) | +|[Get-OBSThermalShader](docs/Get-OBSThermalShader.md) | +|[Get-OBSTransitionKind](docs/Get-OBSTransitionKind.md) | +|[Get-OBSTvCrtSubpixelShader](docs/Get-OBSTvCrtSubpixelShader.md) | +|[Get-OBSTwistShader](docs/Get-OBSTwistShader.md) | +|[Get-OBSTwoPassDropShadowShader](docs/Get-OBSTwoPassDropShadowShader.md) | +|[Get-OBSVCRShader](docs/Get-OBSVCRShader.md) | +|[Get-OBSVersion](docs/Get-OBSVersion.md) | +|[Get-OBSVHSShader](docs/Get-OBSVHSShader.md) | +|[Get-OBSVideoSettings](docs/Get-OBSVideoSettings.md) | +|[Get-OBSVignettingShader](docs/Get-OBSVignettingShader.md) | +|[Get-OBSVirtualCamStatus](docs/Get-OBSVirtualCamStatus.md) | +|[Get-OBSVoronoiPixelationShader](docs/Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](docs/Get-OBSWalkingDeadPixelFixerShader.md) | +|[Get-OBSZigZagShader](docs/Get-OBSZigZagShader.md) | +|[Get-OBSZoomBlurShader](docs/Get-OBSZoomBlurShader.md) | +|[Get-OBSZoomBlurTransitionShader](docs/Get-OBSZoomBlurTransitionShader.md) | +|[Get-OBSZoomShader](docs/Get-OBSZoomShader.md) | +|[Get-OBSZoomXYShader](docs/Get-OBSZoomXYShader.md) | +|[Hide-OBS](docs/Hide-OBS.md) | +|[Import-OBSEffect](docs/Import-OBSEffect.md) | +|[Open-OBSInputFiltersDialog](docs/Open-OBSInputFiltersDialog.md) | +|[Open-OBSInputInteractDialog](docs/Open-OBSInputInteractDialog.md) | +|[Open-OBSInputPropertiesDialog](docs/Open-OBSInputPropertiesDialog.md) | +|[Open-OBSSourceProjector](docs/Open-OBSSourceProjector.md) | +|[Open-OBSVideoMixProjector](docs/Open-OBSVideoMixProjector.md) | +|[Receive-OBS](docs/Receive-OBS.md) | +|[Remove-OBS](docs/Remove-OBS.md) | +|[Remove-OBSEffect](docs/Remove-OBSEffect.md) | +|[Remove-OBSInput](docs/Remove-OBSInput.md) | +|[Remove-OBSProfile](docs/Remove-OBSProfile.md) | +|[Remove-OBSScene](docs/Remove-OBSScene.md) | +|[Remove-OBSSceneItem](docs/Remove-OBSSceneItem.md) | +|[Remove-OBSSourceFilter](docs/Remove-OBSSourceFilter.md) | +|[Resume-OBSRecord](docs/Resume-OBSRecord.md) | +|[Save-OBSReplayBuffer](docs/Save-OBSReplayBuffer.md) | +|[Save-OBSSourceScreenshot](docs/Save-OBSSourceScreenshot.md) | +|[Send-OBS](docs/Send-OBS.md) | +|[Send-OBSCallVendorRequest](docs/Send-OBSCallVendorRequest.md) | +|[Send-OBSCustomEvent](docs/Send-OBSCustomEvent.md) | +|[Send-OBSOffsetMediaInputCursor](docs/Send-OBSOffsetMediaInputCursor.md) | +|[Send-OBSPauseRecord](docs/Send-OBSPauseRecord.md) | +|[Send-OBSPressInputPropertiesButton](docs/Send-OBSPressInputPropertiesButton.md) | +|[Send-OBSSleep](docs/Send-OBSSleep.md) | +|[Send-OBSStreamCaption](docs/Send-OBSStreamCaption.md) | +|[Send-OBSTriggerHotkeyByKeySequence](docs/Send-OBSTriggerHotkeyByKeySequence.md) | +|[Send-OBSTriggerHotkeyByName](docs/Send-OBSTriggerHotkeyByName.md) | +|[Send-OBSTriggerMediaInputAction](docs/Send-OBSTriggerMediaInputAction.md) | +|[Send-OBSTriggerStudioModeTransition](docs/Send-OBSTriggerStudioModeTransition.md) | +|[Set-OBS3DFilter](docs/Set-OBS3DFilter.md) | +|[Set-OBSAudioOutputSource](docs/Set-OBSAudioOutputSource.md) | +|[Set-OBSBrowserSource](docs/Set-OBSBrowserSource.md) | +|[Set-OBSColorFilter](docs/Set-OBSColorFilter.md) | +|[Set-OBSColorSource](docs/Set-OBSColorSource.md) | +|[Set-OBSCurrentPreviewScene](docs/Set-OBSCurrentPreviewScene.md) | +|[Set-OBSCurrentProfile](docs/Set-OBSCurrentProfile.md) | +|[Set-OBSCurrentProgramScene](docs/Set-OBSCurrentProgramScene.md) | +|[Set-OBSCurrentSceneCollection](docs/Set-OBSCurrentSceneCollection.md) | +|[Set-OBSCurrentSceneTransition](docs/Set-OBSCurrentSceneTransition.md) | +|[Set-OBSCurrentSceneTransitionDuration](docs/Set-OBSCurrentSceneTransitionDuration.md) | +|[Set-OBSCurrentSceneTransitionSettings](docs/Set-OBSCurrentSceneTransitionSettings.md) | +|[Set-OBSDisplaySource](docs/Set-OBSDisplaySource.md) | +|[Set-OBSEqualizerFilter](docs/Set-OBSEqualizerFilter.md) | +|[Set-OBSGainFilter](docs/Set-OBSGainFilter.md) | +|[Set-OBSInputAudioBalance](docs/Set-OBSInputAudioBalance.md) | +|[Set-OBSInputAudioMonitorType](docs/Set-OBSInputAudioMonitorType.md) | +|[Set-OBSInputAudioSyncOffset](docs/Set-OBSInputAudioSyncOffset.md) | +|[Set-OBSInputAudioTracks](docs/Set-OBSInputAudioTracks.md) | +|[Set-OBSInputMute](docs/Set-OBSInputMute.md) | +|[Set-OBSInputName](docs/Set-OBSInputName.md) | +|[Set-OBSInputSettings](docs/Set-OBSInputSettings.md) | +|[Set-OBSInputVolume](docs/Set-OBSInputVolume.md) | +|[Set-OBSMarkdownSource](docs/Set-OBSMarkdownSource.md) | +|[Set-OBSMediaInputCursor](docs/Set-OBSMediaInputCursor.md) | +|[Set-OBSMediaSource](docs/Set-OBSMediaSource.md) | +|[Set-OBSOutputSettings](docs/Set-OBSOutputSettings.md) | +|[Set-OBSPersistentData](docs/Set-OBSPersistentData.md) | +|[Set-OBSProfileParameter](docs/Set-OBSProfileParameter.md) | +|[Set-OBSRecordDirectory](docs/Set-OBSRecordDirectory.md) | +|[Set-OBSRenderDelayFilter](docs/Set-OBSRenderDelayFilter.md) | +|[Set-OBSScaleFilter](docs/Set-OBSScaleFilter.md) | +|[Set-OBSSceneItemBlendMode](docs/Set-OBSSceneItemBlendMode.md) | +|[Set-OBSSceneItemEnabled](docs/Set-OBSSceneItemEnabled.md) | +|[Set-OBSSceneItemIndex](docs/Set-OBSSceneItemIndex.md) | +|[Set-OBSSceneItemLocked](docs/Set-OBSSceneItemLocked.md) | +|[Set-OBSSceneItemTransform](docs/Set-OBSSceneItemTransform.md) | +|[Set-OBSSceneName](docs/Set-OBSSceneName.md) | +|[Set-OBSSceneSceneTransitionOverride](docs/Set-OBSSceneSceneTransitionOverride.md) | +|[Set-OBSScrollFilter](docs/Set-OBSScrollFilter.md) | +|[Set-OBSShaderFilter](docs/Set-OBSShaderFilter.md) | +|[Set-OBSSharpnessFilter](docs/Set-OBSSharpnessFilter.md) | +|[Set-OBSSoundCloudSource](docs/Set-OBSSoundCloudSource.md) | +|[Set-OBSSourceFilterEnabled](docs/Set-OBSSourceFilterEnabled.md) | +|[Set-OBSSourceFilterIndex](docs/Set-OBSSourceFilterIndex.md) | +|[Set-OBSSourceFilterName](docs/Set-OBSSourceFilterName.md) | +|[Set-OBSSourceFilterSettings](docs/Set-OBSSourceFilterSettings.md) | +|[Set-OBSStreamServiceSettings](docs/Set-OBSStreamServiceSettings.md) | +|[Set-OBSStudioModeEnabled](docs/Set-OBSStudioModeEnabled.md) | +|[Set-OBSSwitchSource](docs/Set-OBSSwitchSource.md) | +|[Set-OBSTBarPosition](docs/Set-OBSTBarPosition.md) | +|[Set-OBSVideoSettings](docs/Set-OBSVideoSettings.md) | +|[Set-OBSVLCSource](docs/Set-OBSVLCSource.md) | +|[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | +|[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | +|[Show-OBS](docs/Show-OBS.md) | +|[Start-OBSEffect](docs/Start-OBSEffect.md) | +|[Start-OBSOutput](docs/Start-OBSOutput.md) | +|[Start-OBSRecord](docs/Start-OBSRecord.md) | +|[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | +|[Start-OBSStream](docs/Start-OBSStream.md) | +|[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | +|[Stop-OBSEffect](docs/Stop-OBSEffect.md) | +|[Stop-OBSOutput](docs/Stop-OBSOutput.md) | +|[Stop-OBSRecord](docs/Stop-OBSRecord.md) | +|[Stop-OBSReplayBuffer](docs/Stop-OBSReplayBuffer.md) | +|[Stop-OBSStream](docs/Stop-OBSStream.md) | +|[Stop-OBSVirtualCam](docs/Stop-OBSVirtualCam.md) | +|[Switch-OBSInputMute](docs/Switch-OBSInputMute.md) | +|[Switch-OBSOutput](docs/Switch-OBSOutput.md) | +|[Switch-OBSRecord](docs/Switch-OBSRecord.md) | +|[Switch-OBSRecordPause](docs/Switch-OBSRecordPause.md) | +|[Switch-OBSReplayBuffer](docs/Switch-OBSReplayBuffer.md) | +|[Switch-OBSStream](docs/Switch-OBSStream.md) | +|[Switch-OBSVirtualCam](docs/Switch-OBSVirtualCam.md) | +|[Watch-OBS](docs/Watch-OBS.md) | From cdce7c56eb24b073342bfc876e7f480a61066328 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:19:52 +0000 Subject: [PATCH 185/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- obs-powershell.psd1 | 1 + 1 file changed, 1 insertion(+) diff --git a/obs-powershell.psd1 b/obs-powershell.psd1 index fe4adfbb..6549e3ed 100644 --- a/obs-powershell.psd1 +++ b/obs-powershell.psd1 @@ -103,6 +103,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Remove-OBS', 'Send-OBS', 'Show-OBS', +'Stop-OBS', 'Watch-OBS', 'Set-OBS3DFilter', 'Set-OBSColorFilter', From cee1e9e49df68a98ecf9f26f445bc8a2465f9c79 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:09 +0000 Subject: [PATCH 186/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSAudioShader.md | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/docs/Get-OBSAudioShader.md b/docs/Get-OBSAudioShader.md index 627857c6..4cc14640 100644 --- a/docs/Get-OBSAudioShader.md +++ b/docs/Get-OBSAudioShader.md @@ -2,7 +2,6 @@ Get-OBSAudioShader ------------------ ### Synopsis - Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,61 +15,61 @@ Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensi |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |1 |false |audio_magnitude| +|`[Float]`|false |named |False |audio_magnitude| #### **AudioPeak** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |audio_peak| +|`[Float]`|false |named |False |audio_peak| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Intensity** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -86,11 +85,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 684e7549f8404342ce67bba2142cd3b0fd26b002 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:09 +0000 Subject: [PATCH 187/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSAudioShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSAudioShader.json b/docs/_data/Help/Get-OBSAudioShader.json index 3e439275..e116c8b5 100644 --- a/docs/_data/Help/Get-OBSAudioShader.json +++ b/docs/_data/Help/Get-OBSAudioShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 34268669e996f7e18d4050ebebe14e8d4ae8ee62 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:14 +0000 Subject: [PATCH 188/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- ...-OBSDisplacementMapAdvancedInvertShader.md | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Get-OBSDisplacementMapAdvancedInvertShader.md b/docs/Get-OBSDisplacementMapAdvancedInvertShader.md index 94e327ca..b7e40d58 100644 --- a/docs/Get-OBSDisplacementMapAdvancedInvertShader.md +++ b/docs/Get-OBSDisplacementMapAdvancedInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedInvertShader ------------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-Dis |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BackgroundLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |14 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 644a2cdb1ce6fa906fa67fdda83bbfed8bb24ba6 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:14 +0000 Subject: [PATCH 189/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- .../Help/Get-OBSDisplacementMapAdvancedInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json index 02d480c7..46bf6ed5 100644 --- a/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json +++ b/docs/_data/Help/Get-OBSDisplacementMapAdvancedInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From de491dca07ec03048a257d7b0ddd7c18998b5675 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:14 +0000 Subject: [PATCH 190/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSDisplacementMapAdvancedShader.md | 71 +++++++++----------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Get-OBSDisplacementMapAdvancedShader.md b/docs/Get-OBSDisplacementMapAdvancedShader.md index c5a01c96..7d86b0ce 100644 --- a/docs/Get-OBSDisplacementMapAdvancedShader.md +++ b/docs/Get-OBSDisplacementMapAdvancedShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedShader ------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-Displacem |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaskLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |14 |false |mask_layer| +|`[String]`|false |named |False |mask_layer| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From be3fbd7bbbedd86c2f25f2ff42395c8740dfead7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:14 +0000 Subject: [PATCH 191/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json index 30e965e9..2eb87e91 100644 --- a/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json +++ b/docs/_data/Help/Get-OBSDisplacementMapAdvancedShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 232119b67f47d846a570fb0dbe7abd5fce997b53 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:15 +0000 Subject: [PATCH 192/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSDisplacementMapInvertShader.md | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Get-OBSDisplacementMapInvertShader.md b/docs/Get-OBSDisplacementMapInvertShader.md index 15bd7d81..c0b10532 100644 --- a/docs/Get-OBSDisplacementMapInvertShader.md +++ b/docs/Get-OBSDisplacementMapInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapInvertShader ---------------------------------- ### Synopsis - Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-Displacemen |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |3 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 59944c364533e6600102bd9a67a0db742cb515f8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:15 +0000 Subject: [PATCH 193/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSDisplacementMapInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json index 13248875..de771ff7 100644 --- a/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json +++ b/docs/_data/Help/Get-OBSDisplacementMapInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 31856d6249de37147aed918483ff34ef1123ec67 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:15 +0000 Subject: [PATCH 194/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSDisplacementMapShader.md | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Get-OBSDisplacementMapShader.md b/docs/Get-OBSDisplacementMapShader.md index 311c6f56..52eaa8b9 100644 --- a/docs/Get-OBSDisplacementMapShader.md +++ b/docs/Get-OBSDisplacementMapShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapShader ---------------------------- ### Synopsis - Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 4f6fd49a632e7c6f731fbc6c8651ea2724fd81cb Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:15 +0000 Subject: [PATCH 195/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSDisplacementMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSDisplacementMapShader.json b/docs/_data/Help/Get-OBSDisplacementMapShader.json index 82fe9189..a49bf9e4 100644 --- a/docs/_data/Help/Get-OBSDisplacementMapShader.json +++ b/docs/_data/Help/Get-OBSDisplacementMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 0ddcb4ebf439b03fab1b569bcf20fe6e76b4b5ba Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:19 +0000 Subject: [PATCH 196/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSGlitchPeriodicShader.md | 49 +++++++++++++---------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/Get-OBSGlitchPeriodicShader.md b/docs/Get-OBSGlitchPeriodicShader.md index 330bc10b..14fd3235 100644 --- a/docs/Get-OBSGlitchPeriodicShader.md +++ b/docs/Get-OBSGlitchPeriodicShader.md @@ -2,7 +2,6 @@ Get-OBSGlitchPeriodicShader --------------------------- ### Synopsis - Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **DURA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | - -#### **PERI** - -|Type |Required|Position|PipelineInput| -|---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Float]`|false |named |False | #### **SCRA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From caaf0f5a441c68c001f17accf992d19fe41de69d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:20 +0000 Subject: [PATCH 197/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSGlitchPeriodicShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSGlitchPeriodicShader.json b/docs/_data/Help/Get-OBSGlitchPeriodicShader.json index 08609385..7e4a2349 100644 --- a/docs/_data/Help/Get-OBSGlitchPeriodicShader.json +++ b/docs/_data/Help/Get-OBSGlitchPeriodicShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 3ef1cc5363b745885e904f3904a9f6357746624c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:23 +0000 Subject: [PATCH 198/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSNoiseShader.md | 51 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Get-OBSNoiseShader.md b/docs/Get-OBSNoiseShader.md index 6fa983dc..b75ddd04 100644 --- a/docs/Get-OBSNoiseShader.md +++ b/docs/Get-OBSNoiseShader.md @@ -2,7 +2,6 @@ Get-OBSNoiseShader ------------------ ### Synopsis - Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,75 +13,75 @@ Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| - -#### **Force** - |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[String]`|false |named |False | -#### **Monochromatic** +#### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | -#### **NoResponse** +#### **Monochromatic** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoiseLevel** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Scale** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Speed** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseRand** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------| -|`[switch]`|false |Named |false |use_rand| +|`[Switch]`|false |named |False |use_rand| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [-Monochromatic ] [-UseRand ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 6cb2ac720195e6e584b03c0e021c9c262edfb722 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:23 +0000 Subject: [PATCH 199/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSNoiseShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSNoiseShader.json b/docs/_data/Help/Get-OBSNoiseShader.json index db2627eb..075a0ecc 100644 --- a/docs/_data/Help/Get-OBSNoiseShader.json +++ b/docs/_data/Help/Get-OBSNoiseShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 6779c9b4e4cbc28fc47d7db199ad4491906afdf8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:23 +0000 Subject: [PATCH 200/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSNormalMapShader.md | 43 ++++++++++++++-------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Get-OBSNormalMapShader.md b/docs/Get-OBSNormalMapShader.md index ef22fc4d..245b4832 100644 --- a/docs/Get-OBSNormalMapShader.md +++ b/docs/Get-OBSNormalMapShader.md @@ -2,7 +2,6 @@ Get-OBSNormalMapShader ---------------------- ### Synopsis - Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [-OffsetHeight ] [-InvertR ] [-InvertG ] [-InvertH ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 162785244e4162264d7663e7d087b16da2ecff7a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:23 +0000 Subject: [PATCH 201/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSNormalMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSNormalMapShader.json b/docs/_data/Help/Get-OBSNormalMapShader.json index 31f25d5e..7464c72a 100644 --- a/docs/_data/Help/Get-OBSNormalMapShader.json +++ b/docs/_data/Help/Get-OBSNormalMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 7c38d929f6dd8ad7ed3f047866b90ff3159e5d69 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:25 +0000 Subject: [PATCH 202/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSQuadrilateralCropShader.md | 47 +++++++++++--------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/Get-OBSQuadrilateralCropShader.md b/docs/Get-OBSQuadrilateralCropShader.md index 611c85ce..c5037d7a 100644 --- a/docs/Get-OBSQuadrilateralCropShader.md +++ b/docs/Get-OBSQuadrilateralCropShader.md @@ -2,7 +2,6 @@ Get-OBSQuadrilateralCropShader ------------------------------ ### Synopsis - Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,91 +15,91 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-To |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |4 |false |Bottom_Left_X| +|`[Float]`|false |named |False |Bottom_Left_X| #### **BottomLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |5 |false |Bottom_Left_Y| +|`[Float]`|false |named |False |Bottom_Left_Y| #### **BottomRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |6 |false |Bottom_Right_X| +|`[Float]`|false |named |False |Bottom_Right_X| #### **BottomRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |7 |false |Bottom_Right_Y| +|`[Float]`|false |named |False |Bottom_Right_Y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |9 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |10 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **TopLeftX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |Top_Left_X| +|`[Float]`|false |named |False |Top_Left_X| #### **TopLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |1 |false |Top_Left_Y| +|`[Float]`|false |named |False |Top_Left_Y| #### **TopRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |2 |false |Top_Right_X| +|`[Float]`|false |named |False |Top_Right_X| #### **TopRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |3 |false |Top_Right_Y| +|`[Float]`|false |named |False |Top_Right_Y| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -116,11 +115,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 6e1e48823d7dbba60de3c7fba4ceb4c0ab48ecf9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:25 +0000 Subject: [PATCH 203/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSQuadrilateralCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSQuadrilateralCropShader.json b/docs/_data/Help/Get-OBSQuadrilateralCropShader.json index e0609f0e..bf3337fe 100644 --- a/docs/_data/Help/Get-OBSQuadrilateralCropShader.json +++ b/docs/_data/Help/Get-OBSQuadrilateralCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 3a05768bbff4055477e03458861f9a88b8413f69 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:33 +0000 Subject: [PATCH 204/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Get-OBSZoomBlurTransitionShader.md | 41 ++++++++++--------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/docs/Get-OBSZoomBlurTransitionShader.md b/docs/Get-OBSZoomBlurTransitionShader.md index 5859f433..b28ab22b 100644 --- a/docs/Get-OBSZoomBlurTransitionShader.md +++ b/docs/Get-OBSZoomBlurTransitionShader.md @@ -2,7 +2,6 @@ Get-OBSZoomBlurTransitionShader ------------------------------- ### Synopsis - Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-Tra |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[switch]`|false |Named |false |convert_linear| +|`[Switch]`|false |named |False |convert_linear| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ImageA** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |0 |false |image_a| +|`[String]`|false |named |False |image_a| #### **ImageB** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |1 |false |image_b| +|`[String]`|false |named |False |image_b| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **TransitionTime** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |2 |false |transition_time| +|`[Float]`|false |named |False |transition_time| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [-ConvertLinear ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From a1226e23345a20970084baec1f5cc26705b3a16a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:33 +0000 Subject: [PATCH 205/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Get-OBSZoomBlurTransitionShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json index c14ca126..a23dbcaf 100644 --- a/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json +++ b/docs/_data/Help/Get-OBSZoomBlurTransitionShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From f396687e1e433d3ff6439f5356453fa0ea7927a3 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:42 +0000 Subject: [PATCH 206/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Stop-OBS.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/Stop-OBS.md diff --git a/docs/Stop-OBS.md b/docs/Stop-OBS.md new file mode 100644 index 00000000..64e40861 --- /dev/null +++ b/docs/Stop-OBS.md @@ -0,0 +1,130 @@ +Stop-OBS +-------- + +### Synopsis +Stops OBS + +--- + +### Description + +Stops OBS. + +By default, stops recording and streaming. + +If -Process is provided, will stop all running OBS processes + +If -Recording is provided, will stop recording +If -Streaming if provided, will stop obs streaming +If -VirtualCamera is provided, will stop the virtual camera + +--- + +### Related Links +* [Stop-OBSRecord](Stop-OBSRecord.md) + +* [Stop-OBSStream](Stop-OBSStream.md) + +* [Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) + +* [Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) + +--- + +### Examples +Stop Streaming and Recording + +```PowerShell +Stop-OBS +``` +Stops obs recording + +```PowerShell +Stop-OBS -Recording +``` +Stop Streaming + +```PowerShell +Stop-OBS -Streaming +``` +Stop OBS Virtual Camera + +```PowerShell +Stop-OBS -VirtualCamera +``` +Stop OBS Virtual Camera without prompting + +```PowerShell +Stop-OBS -VirtualCamera -Confirm:$false +``` +> EXAMPLE 6 + +```PowerShell +Stop-OBS -StudioMode +``` + +--- + +### Parameters +#### **Recording** +If set, will stop recording. + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **Streaming** +If set, will stop streaming + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **VirtualCamera** +If set, will stop the virtual camera. + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **Process** +If set, will stop the OBS process. + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **StudioMode** +If set, will enable studio mode. + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **WhatIf** +-WhatIf is an automatic variable that is created when a command has ```[CmdletBinding(SupportsShouldProcess)]```. +-WhatIf is used to see what would happen, or return operations without executing them +#### **Confirm** +-Confirm is an automatic variable that is created when a command has ```[CmdletBinding(SupportsShouldProcess)]```. +-Confirm is used to -Confirm each operation. + +If you pass ```-Confirm:$false``` you will not be prompted. + +If the command sets a ```[ConfirmImpact("Medium")]``` which is lower than ```$confirmImpactPreference```, you will not be prompted unless -Confirm is passed. + +--- + +### Notes +This command Supports Should Process and has a ConfirmImpact of 'High' + +In an interactive session, this command prompt by default. + +If `-WhatIf` is passed, will output what happen if this ran +If `-Confirm:$false`, confirmation will be skipped. + +--- + +### Syntax +```PowerShell +Stop-OBS [-Recording] [-Streaming] [-VirtualCamera] [-Process] [-StudioMode] [-WhatIf] [-Confirm] [] +``` From 75fcea8775a240f34fc5c61b76693d9083dbea98 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:42 +0000 Subject: [PATCH 207/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Stop-OBS.json | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/_data/Help/Stop-OBS.json diff --git a/docs/_data/Help/Stop-OBS.json b/docs/_data/Help/Stop-OBS.json new file mode 100644 index 00000000..dbe33e14 --- /dev/null +++ b/docs/_data/Help/Stop-OBS.json @@ -0,0 +1,69 @@ +{ + "Synopsis": "Stops OBS", + "Description": "Stops OBS.\n\nBy default, stops recording and streaming.\n\nIf -Process is provided, will stop all running OBS processes\n\nIf -Recording is provided, will stop recording\nIf -Streaming if provided, will stop obs streaming\nIf -VirtualCamera is provided, will stop the virtual camera", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + "This command Supports Should Process and has a ConfirmImpact of 'High'\n\nIn an interactive session, this command prompt by default.\n\nIf `-WhatIf` is passed, will output what happen if this ran\nIf `-Confirm:$false`, confirmation will be skipped." + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + null + ], + "Outputs": [ + null + ], + "Links": [ + null, + null, + null, + null + ], + "Examples": [ + { + "Title": "EXAMPLE 1", + "Markdown": "Stop Streaming and Recording", + "Code": "Stop-OBS" + }, + { + "Title": "EXAMPLE 2", + "Markdown": "Stops obs recording", + "Code": "Stop-OBS -Recording" + }, + { + "Title": "EXAMPLE 3", + "Markdown": "Stop Streaming ", + "Code": "Stop-OBS -Streaming" + }, + { + "Title": "EXAMPLE 4", + "Markdown": "Stop OBS Virtual Camera", + "Code": "Stop-OBS -VirtualCamera" + }, + { + "Title": "EXAMPLE 5", + "Markdown": "Stop OBS Virtual Camera without prompting", + "Code": "Stop-OBS -VirtualCamera -Confirm:$false" + }, + { + "Title": "EXAMPLE 6", + "Markdown": "", + "Code": "Stop-OBS -StudioMode" + } + ] +} \ No newline at end of file From 6971b6f67ed358946ef5a8ec3c7df2752e1c3d0a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:45 +0000 Subject: [PATCH 208/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSAudioShader.md | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/docs/Add-OBSAudioShader.md b/docs/Add-OBSAudioShader.md index 627857c6..4cc14640 100644 --- a/docs/Add-OBSAudioShader.md +++ b/docs/Add-OBSAudioShader.md @@ -2,7 +2,6 @@ Get-OBSAudioShader ------------------ ### Synopsis - Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,61 +15,61 @@ Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensi |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |1 |false |audio_magnitude| +|`[Float]`|false |named |False |audio_magnitude| #### **AudioPeak** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |audio_peak| +|`[Float]`|false |named |False |audio_peak| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Intensity** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -86,11 +85,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From e1ed2c9f67db934be1610c5e8fdd4c550e85b9a0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:45 +0000 Subject: [PATCH 209/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSAudioShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSAudioShader.json b/docs/_data/Help/Add-OBSAudioShader.json index 3e439275..e116c8b5 100644 --- a/docs/_data/Help/Add-OBSAudioShader.json +++ b/docs/_data/Help/Add-OBSAudioShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From e4230d07b9d391840d86bd42c5c9563511e43fb8 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 210/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- ...-OBSDisplacementMapAdvancedInvertShader.md | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Add-OBSDisplacementMapAdvancedInvertShader.md b/docs/Add-OBSDisplacementMapAdvancedInvertShader.md index 94e327ca..b7e40d58 100644 --- a/docs/Add-OBSDisplacementMapAdvancedInvertShader.md +++ b/docs/Add-OBSDisplacementMapAdvancedInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedInvertShader ------------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-Dis |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BackgroundLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |14 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 6d56feaee8d439d8d216b45caef109cd9c22443a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 211/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- .../Help/Add-OBSDisplacementMapAdvancedInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json index 02d480c7..46bf6ed5 100644 --- a/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json +++ b/docs/_data/Help/Add-OBSDisplacementMapAdvancedInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From a2a76422b25a56de9bbfbcc98e9d9269d0a4ec72 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 212/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSDisplacementMapAdvancedShader.md | 71 +++++++++----------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Add-OBSDisplacementMapAdvancedShader.md b/docs/Add-OBSDisplacementMapAdvancedShader.md index c5a01c96..7d86b0ce 100644 --- a/docs/Add-OBSDisplacementMapAdvancedShader.md +++ b/docs/Add-OBSDisplacementMapAdvancedShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedShader ------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-Displacem |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaskLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |14 |false |mask_layer| +|`[String]`|false |named |False |mask_layer| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 9afb3b139a38b1512a358978b8caeea0c694e1da Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 213/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json index 30e965e9..2eb87e91 100644 --- a/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json +++ b/docs/_data/Help/Add-OBSDisplacementMapAdvancedShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 1e3d1493df349c225c64ac35e29ac433c52afbee Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 214/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSDisplacementMapInvertShader.md | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Add-OBSDisplacementMapInvertShader.md b/docs/Add-OBSDisplacementMapInvertShader.md index 15bd7d81..c0b10532 100644 --- a/docs/Add-OBSDisplacementMapInvertShader.md +++ b/docs/Add-OBSDisplacementMapInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapInvertShader ---------------------------------- ### Synopsis - Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-Displacemen |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |3 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From e5954a2145bfb4c005402c9ec682cc6f45d352d4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 215/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSDisplacementMapInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json index 13248875..de771ff7 100644 --- a/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json +++ b/docs/_data/Help/Add-OBSDisplacementMapInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 42d75c8a13903e6cdbac54c4a20037cdcb51cbe1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 216/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSDisplacementMapShader.md | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Add-OBSDisplacementMapShader.md b/docs/Add-OBSDisplacementMapShader.md index 311c6f56..52eaa8b9 100644 --- a/docs/Add-OBSDisplacementMapShader.md +++ b/docs/Add-OBSDisplacementMapShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapShader ---------------------------- ### Synopsis - Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From ed4d9cc265ce2a00a610ad3b1b18a23f380aaf30 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:48 +0000 Subject: [PATCH 217/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSDisplacementMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSDisplacementMapShader.json b/docs/_data/Help/Add-OBSDisplacementMapShader.json index 82fe9189..a49bf9e4 100644 --- a/docs/_data/Help/Add-OBSDisplacementMapShader.json +++ b/docs/_data/Help/Add-OBSDisplacementMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 350c8b457339dec5f7a80e707b9145bc28294d5e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:52 +0000 Subject: [PATCH 218/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSGlitchPeriodicShader.md | 49 +++++++++++++---------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/Add-OBSGlitchPeriodicShader.md b/docs/Add-OBSGlitchPeriodicShader.md index 330bc10b..14fd3235 100644 --- a/docs/Add-OBSGlitchPeriodicShader.md +++ b/docs/Add-OBSGlitchPeriodicShader.md @@ -2,7 +2,6 @@ Get-OBSGlitchPeriodicShader --------------------------- ### Synopsis - Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **DURA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | - -#### **PERI** - -|Type |Required|Position|PipelineInput| -|---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Float]`|false |named |False | #### **SCRA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 24bc69fabac8e85196e38efe68843d8209061f92 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:52 +0000 Subject: [PATCH 219/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSGlitchPeriodicShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSGlitchPeriodicShader.json b/docs/_data/Help/Add-OBSGlitchPeriodicShader.json index 08609385..7e4a2349 100644 --- a/docs/_data/Help/Add-OBSGlitchPeriodicShader.json +++ b/docs/_data/Help/Add-OBSGlitchPeriodicShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 2b36e627fd3745896586cb800e2f5f9288b5b4b1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:54 +0000 Subject: [PATCH 220/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSNoiseShader.md | 51 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Add-OBSNoiseShader.md b/docs/Add-OBSNoiseShader.md index 6fa983dc..b75ddd04 100644 --- a/docs/Add-OBSNoiseShader.md +++ b/docs/Add-OBSNoiseShader.md @@ -2,7 +2,6 @@ Get-OBSNoiseShader ------------------ ### Synopsis - Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,75 +13,75 @@ Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| - -#### **Force** - |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[String]`|false |named |False | -#### **Monochromatic** +#### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | -#### **NoResponse** +#### **Monochromatic** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoiseLevel** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Scale** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Speed** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseRand** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------| -|`[switch]`|false |Named |false |use_rand| +|`[Switch]`|false |named |False |use_rand| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [-Monochromatic ] [-UseRand ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 8cfda76d200724d57ab8aa8864b78e16f9fc06cf Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:54 +0000 Subject: [PATCH 221/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSNoiseShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSNoiseShader.json b/docs/_data/Help/Add-OBSNoiseShader.json index db2627eb..075a0ecc 100644 --- a/docs/_data/Help/Add-OBSNoiseShader.json +++ b/docs/_data/Help/Add-OBSNoiseShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 2861730c99064d51c5e8cfb1e1079924dff66950 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:54 +0000 Subject: [PATCH 222/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSNormalMapShader.md | 43 ++++++++++++++-------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Add-OBSNormalMapShader.md b/docs/Add-OBSNormalMapShader.md index ef22fc4d..245b4832 100644 --- a/docs/Add-OBSNormalMapShader.md +++ b/docs/Add-OBSNormalMapShader.md @@ -2,7 +2,6 @@ Get-OBSNormalMapShader ---------------------- ### Synopsis - Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [-OffsetHeight ] [-InvertR ] [-InvertG ] [-InvertH ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 4142b23c2aa8c28ee0a2265c29a3ca4363204e9a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:54 +0000 Subject: [PATCH 223/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSNormalMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSNormalMapShader.json b/docs/_data/Help/Add-OBSNormalMapShader.json index 31f25d5e..7464c72a 100644 --- a/docs/_data/Help/Add-OBSNormalMapShader.json +++ b/docs/_data/Help/Add-OBSNormalMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 93ce65383aa154fc55172dea45f2b1999cdd4e95 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:55 +0000 Subject: [PATCH 224/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSQuadrilateralCropShader.md | 47 +++++++++++--------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/Add-OBSQuadrilateralCropShader.md b/docs/Add-OBSQuadrilateralCropShader.md index 611c85ce..c5037d7a 100644 --- a/docs/Add-OBSQuadrilateralCropShader.md +++ b/docs/Add-OBSQuadrilateralCropShader.md @@ -2,7 +2,6 @@ Get-OBSQuadrilateralCropShader ------------------------------ ### Synopsis - Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,91 +15,91 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-To |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |4 |false |Bottom_Left_X| +|`[Float]`|false |named |False |Bottom_Left_X| #### **BottomLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |5 |false |Bottom_Left_Y| +|`[Float]`|false |named |False |Bottom_Left_Y| #### **BottomRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |6 |false |Bottom_Right_X| +|`[Float]`|false |named |False |Bottom_Right_X| #### **BottomRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |7 |false |Bottom_Right_Y| +|`[Float]`|false |named |False |Bottom_Right_Y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |9 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |10 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **TopLeftX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |Top_Left_X| +|`[Float]`|false |named |False |Top_Left_X| #### **TopLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |1 |false |Top_Left_Y| +|`[Float]`|false |named |False |Top_Left_Y| #### **TopRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |2 |false |Top_Right_X| +|`[Float]`|false |named |False |Top_Right_X| #### **TopRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |3 |false |Top_Right_Y| +|`[Float]`|false |named |False |Top_Right_Y| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -116,11 +115,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 841a556a855fd165f450385f4216130aac465f16 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:20:55 +0000 Subject: [PATCH 225/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSQuadrilateralCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSQuadrilateralCropShader.json b/docs/_data/Help/Add-OBSQuadrilateralCropShader.json index e0609f0e..bf3337fe 100644 --- a/docs/_data/Help/Add-OBSQuadrilateralCropShader.json +++ b/docs/_data/Help/Add-OBSQuadrilateralCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 614990a756c2a5ce20da3843408feac820b0d2a0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:00 +0000 Subject: [PATCH 226/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Add-OBSZoomBlurTransitionShader.md | 41 ++++++++++--------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/docs/Add-OBSZoomBlurTransitionShader.md b/docs/Add-OBSZoomBlurTransitionShader.md index 5859f433..b28ab22b 100644 --- a/docs/Add-OBSZoomBlurTransitionShader.md +++ b/docs/Add-OBSZoomBlurTransitionShader.md @@ -2,7 +2,6 @@ Get-OBSZoomBlurTransitionShader ------------------------------- ### Synopsis - Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-Tra |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[switch]`|false |Named |false |convert_linear| +|`[Switch]`|false |named |False |convert_linear| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ImageA** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |0 |false |image_a| +|`[String]`|false |named |False |image_a| #### **ImageB** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |1 |false |image_b| +|`[String]`|false |named |False |image_b| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **TransitionTime** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |2 |false |transition_time| +|`[Float]`|false |named |False |transition_time| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [-ConvertLinear ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 1e9ac6e7b5f9096a9d6ecb3f55534ece2701fef0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:00 +0000 Subject: [PATCH 227/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Add-OBSZoomBlurTransitionShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json index c14ca126..a23dbcaf 100644 --- a/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json +++ b/docs/_data/Help/Add-OBSZoomBlurTransitionShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 244c566410c839e36161d4dafcb9220a3d0d63e0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:11 +0000 Subject: [PATCH 228/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSAudioShader.md | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/docs/Set-OBSAudioShader.md b/docs/Set-OBSAudioShader.md index 627857c6..4cc14640 100644 --- a/docs/Set-OBSAudioShader.md +++ b/docs/Set-OBSAudioShader.md @@ -2,7 +2,6 @@ Get-OBSAudioShader ------------------ ### Synopsis - Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,61 +15,61 @@ Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensi |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |1 |false |audio_magnitude| +|`[Float]`|false |named |False |audio_magnitude| #### **AudioPeak** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |audio_peak| +|`[Float]`|false |named |False |audio_peak| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Intensity** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -86,11 +85,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSAudioShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From ebc19ef8ca3f60a0e679c023ca211a8d06a41725 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:11 +0000 Subject: [PATCH 229/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSAudioShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSAudioShader.json b/docs/_data/Help/Set-OBSAudioShader.json index 3e439275..e116c8b5 100644 --- a/docs/_data/Help/Set-OBSAudioShader.json +++ b/docs/_data/Help/Set-OBSAudioShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSAudioShader [[-AudioPeak] ] [[-AudioMagnitude] ] [[-Intensity] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 5b3f5656ffcc690dfd372bb921512af1bbcc3eec Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 230/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- ...-OBSDisplacementMapAdvancedInvertShader.md | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Set-OBSDisplacementMapAdvancedInvertShader.md b/docs/Set-OBSDisplacementMapAdvancedInvertShader.md index 94e327ca..b7e40d58 100644 --- a/docs/Set-OBSDisplacementMapAdvancedInvertShader.md +++ b/docs/Set-OBSDisplacementMapAdvancedInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedInvertShader ------------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-Dis |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BackgroundLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |14 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 804503f7eb9bf4a20b963bbd14cb77bae4967de5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 231/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- .../Help/Set-OBSDisplacementMapAdvancedInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json b/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json index 02d480c7..46bf6ed5 100644 --- a/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json +++ b/docs/_data/Help/Set-OBSDisplacementMapAdvancedInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 6e3fe26c43b83183cd79b28e86ea88e78e0c8c13 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 232/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSDisplacementMapAdvancedShader.md | 71 +++++++++----------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/Set-OBSDisplacementMapAdvancedShader.md b/docs/Set-OBSDisplacementMapAdvancedShader.md index c5a01c96..7d86b0ce 100644 --- a/docs/Set-OBSDisplacementMapAdvancedShader.md +++ b/docs/Set-OBSDisplacementMapAdvancedShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapAdvancedShader ------------------------------------ ### Synopsis - Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,163 +15,163 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-Displacem |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------------| -|`[switch]`|false |Named |false |alpha_affects_strength| +|`[Switch]`|false |named |False |alpha_affects_strength| #### **ApplyAlpha** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------| -|`[switch]`|false |Named |false |apply_alpha| +|`[Switch]`|false |named |False |apply_alpha| #### **BlueAffectsBlur** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[switch]`|false |Named |false |blue_affects_blur| +|`[Switch]`|false |named |False |blue_affects_blur| #### **BlueAffectsColorize** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_colorize| +|`[Switch]`|false |named |False |blue_affects_colorize| #### **BlueAffectsStrength** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------------------| -|`[switch]`|false |Named |false |blue_affects_strength| +|`[Switch]`|false |named |False |blue_affects_strength| #### **BlurAngle** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |8 |false |blur_angle| +|`[Float]`|false |named |False |blur_angle| #### **BlurDirections** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |7 |false |blur_directions| +|`[Float]`|false |named |False |blur_directions| #### **BlurInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|---------| -|`[string]`|false |4 |false |blur_info| +|`[String]`|false |named |False |blur_info| #### **BlurQuality** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|------------| -|`[float]`|false |6 |false |blur_quality| +|`[Float]`|false |named |False |blur_quality| #### **BlurSize** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------| -|`[float]`|false |5 |false |blur_size| +|`[Float]`|false |named |False |blur_size| #### **ChromaticAberration** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------------| -|`[float]`|false |10 |false |chromatic_aberration| +|`[Float]`|false |named |False |chromatic_aberration| #### **ChromaticAberrationInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------------------| -|`[string]`|false |9 |false |chromatic_aberration_info| +|`[String]`|false |named |False |chromatic_aberration_info| #### **ColorizeColor** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[string]`|false |12 |false |colorize_color| +|`[String]`|false |named |False |colorize_color| #### **ColorizeInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |11 |false |colorize_info| +|`[String]`|false |named |False |colorize_info| #### **DisplacementCurve** |Type |Required|Position|PipelineInput|Aliases | |-------|--------|--------|-------------|------------------| -|`[int]`|false |3 |false |displacement_curve| +|`[Int]`|false |named |False |displacement_curve| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |16 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **FlagsInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |13 |false |flags_info| +|`[String]`|false |named |False |flags_info| #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **MaskLayer** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------| -|`[string]`|false |14 |false |mask_layer| +|`[String]`|false |named |False |mask_layer| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |17 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |15 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -188,11 +187,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapAdvancedShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [-BlueAffectsStrength ] [-BlueAffectsColorize ] [-BlueAffectsBlur ] [-AlphaAffectsStrength ] [-ApplyAlpha ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From cb44f580e048f96d8dc25121bd3d26d3a8d1d3a9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 233/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json b/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json index 30e965e9..2eb87e91 100644 --- a/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json +++ b/docs/_data/Help/Set-OBSDisplacementMapAdvancedShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-DisplacementCurve] ] [[-BlurInfo] ] [[-BlurSize] ] [[-BlurQuality] ] [[-BlurDirections] ] [[-BlurAngle] ] [[-ChromaticAberrationInfo] ] [[-ChromaticAberration] ] [[-ColorizeInfo] ] [[-ColorizeColor] ] [[-FlagsInfo] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From daceadde2ad948b42d1876ab08fcf0055f467eca Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 234/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSDisplacementMapInvertShader.md | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Set-OBSDisplacementMapInvertShader.md b/docs/Set-OBSDisplacementMapInvertShader.md index 15bd7d81..c0b10532 100644 --- a/docs/Set-OBSDisplacementMapInvertShader.md +++ b/docs/Set-OBSDisplacementMapInvertShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapInvertShader ---------------------------------- ### Synopsis - Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-Displacemen |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|----------------| -|`[string]`|false |3 |false |background_layer| +|`[String]`|false |named |False |background_layer| #### **DisplacementInfo** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-----------------| -|`[string]`|false |0 |false |displacement_info| +|`[String]`|false |named |False |displacement_info| #### **DisplacementX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |1 |false |displacement_x| +|`[Float]`|false |named |False |displacement_x| #### **DisplacementY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |2 |false |displacement_y| +|`[Float]`|false |named |False |displacement_y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSDisplacementMapInvertShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 0f8a3d6e54013caa85bed0e23d23ac8f0b0b855f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 235/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSDisplacementMapInvertShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json b/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json index 13248875..de771ff7 100644 --- a/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json +++ b/docs/_data/Help/Set-OBSDisplacementMapInvertShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-BackgroundLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 4f86a9cd8efcb06dbbce441627dd3488ab8bf738 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 236/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSDisplacementMapShader.md | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/Set-OBSDisplacementMapShader.md b/docs/Set-OBSDisplacementMapShader.md index 311c6f56..52eaa8b9 100644 --- a/docs/Set-OBSDisplacementMapShader.md +++ b/docs/Set-OBSDisplacementMapShader.md @@ -2,7 +2,6 @@ Get-OBSDisplacementMapShader ---------------------------- ### Synopsis - Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From c95e0a5af50555b25f2969dff723c2316eabe8c1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:14 +0000 Subject: [PATCH 237/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSDisplacementMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSDisplacementMapShader.json b/docs/_data/Help/Set-OBSDisplacementMapShader.json index 82fe9189..a49bf9e4 100644 --- a/docs/_data/Help/Set-OBSDisplacementMapShader.json +++ b/docs/_data/Help/Set-OBSDisplacementMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSDisplacementMapShader [[-DisplacementInfo] ] [[-DisplacementX] ] [[-DisplacementY] ] [[-MaskLayer] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From f53ee2b37631803a16c698166fda037d9617eb3f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:17 +0000 Subject: [PATCH 238/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSGlitchPeriodicShader.md | 49 +++++++++++++---------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/Set-OBSGlitchPeriodicShader.md b/docs/Set-OBSGlitchPeriodicShader.md index 330bc10b..14fd3235 100644 --- a/docs/Set-OBSGlitchPeriodicShader.md +++ b/docs/Set-OBSGlitchPeriodicShader.md @@ -2,7 +2,6 @@ Get-OBSGlitchPeriodicShader --------------------------- ### Synopsis - Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,67 +15,67 @@ Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | #### **DURA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | - -#### **PERI** - -|Type |Required|Position|PipelineInput| -|---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | + +#### **PERI** + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Float]`|false |named |False | #### **SCRA** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -92,11 +91,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSGlitchPeriodicShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From c4fab96b81a00620fb723f369506ee80f5049e08 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:17 +0000 Subject: [PATCH 239/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSGlitchPeriodicShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSGlitchPeriodicShader.json b/docs/_data/Help/Set-OBSGlitchPeriodicShader.json index 08609385..7e4a2349 100644 --- a/docs/_data/Help/Set-OBSGlitchPeriodicShader.json +++ b/docs/_data/Help/Set-OBSGlitchPeriodicShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSGlitchPeriodicShader [[-PERI] ] [[-DURA] ] [[-AMPL] ] [[-SCRA] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 58b9a2af90bfe30ca9d67efdeac06ffea2165aeb Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:19 +0000 Subject: [PATCH 240/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSNoiseShader.md | 51 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/Set-OBSNoiseShader.md b/docs/Set-OBSNoiseShader.md index 6fa983dc..b75ddd04 100644 --- a/docs/Set-OBSNoiseShader.md +++ b/docs/Set-OBSNoiseShader.md @@ -2,7 +2,6 @@ Get-OBSNoiseShader ------------------ ### Synopsis - Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,75 +13,75 @@ Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] ### Parameters #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |4 |true (ByPropertyName)| - -#### **Force** - |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[String]`|false |named |False | -#### **Monochromatic** +#### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | -#### **NoResponse** +#### **Monochromatic** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoiseLevel** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |2 |false | +|`[Float]`|false |named |False | + +#### **NoResponse** + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **Scale** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |1 |false | +|`[Float]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |5 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |3 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Speed** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |0 |false | +|`[Float]`|false |named |False | #### **UseRand** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------| -|`[switch]`|false |Named |false |use_rand| +|`[Switch]`|false |named |False |use_rand| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSNoiseShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [-Monochromatic ] [-UseRand ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From d5b87866a56a020098b6499ffedafaa40b5cca9a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:19 +0000 Subject: [PATCH 241/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSNoiseShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSNoiseShader.json b/docs/_data/Help/Set-OBSNoiseShader.json index db2627eb..075a0ecc 100644 --- a/docs/_data/Help/Set-OBSNoiseShader.json +++ b/docs/_data/Help/Set-OBSNoiseShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNoiseShader [[-Speed] ] [[-Scale] ] [[-NoiseLevel] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 89916cd6a6b05de3e6a7ccaaa5acac2bc10bcb87 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:19 +0000 Subject: [PATCH 242/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSNormalMapShader.md | 43 ++++++++++++++-------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/Set-OBSNormalMapShader.md b/docs/Set-OBSNormalMapShader.md index ef22fc4d..245b4832 100644 --- a/docs/Set-OBSNormalMapShader.md +++ b/docs/Set-OBSNormalMapShader.md @@ -2,7 +2,6 @@ Get-OBSNormalMapShader ---------------------- ### Synopsis - Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -14,81 +13,81 @@ Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [-OffsetHeight ] [-InvertR ] [-InvertG ] [-InvertH ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From 8ce9a3dbd38bf35ea26f7113b359396e3758f48c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:19 +0000 Subject: [PATCH 243/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSNormalMapShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSNormalMapShader.json b/docs/_data/Help/Set-OBSNormalMapShader.json index 31f25d5e..7464c72a 100644 --- a/docs/_data/Help/Set-OBSNormalMapShader.json +++ b/docs/_data/Help/Set-OBSNormalMapShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSNormalMapShader [[-Strength] ] [[-Type] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From a6926305901e847c396df76cb85268e2298abf63 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:20 +0000 Subject: [PATCH 244/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSQuadrilateralCropShader.md | 47 +++++++++++--------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/Set-OBSQuadrilateralCropShader.md b/docs/Set-OBSQuadrilateralCropShader.md index 611c85ce..c5037d7a 100644 --- a/docs/Set-OBSQuadrilateralCropShader.md +++ b/docs/Set-OBSQuadrilateralCropShader.md @@ -2,7 +2,6 @@ Get-OBSQuadrilateralCropShader ------------------------------ ### Synopsis - Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,91 +15,91 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-To |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |4 |false |Bottom_Left_X| +|`[Float]`|false |named |False |Bottom_Left_X| #### **BottomLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-------------| -|`[float]`|false |5 |false |Bottom_Left_Y| +|`[Float]`|false |named |False |Bottom_Left_Y| #### **BottomRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |6 |false |Bottom_Right_X| +|`[Float]`|false |named |False |Bottom_Right_X| #### **BottomRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|--------------| -|`[float]`|false |7 |false |Bottom_Right_Y| +|`[Float]`|false |named |False |Bottom_Right_Y| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |9 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |10 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |8 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **TopLeftX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |0 |false |Top_Left_X| +|`[Float]`|false |named |False |Top_Left_X| #### **TopLeftY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|----------| -|`[float]`|false |1 |false |Top_Left_Y| +|`[Float]`|false |named |False |Top_Left_Y| #### **TopRightX** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |2 |false |Top_Right_X| +|`[Float]`|false |named |False |Top_Right_X| #### **TopRightY** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|-----------| -|`[float]`|false |3 |false |Top_Right_Y| +|`[Float]`|false |named |False |Top_Right_Y| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -116,11 +115,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSQuadrilateralCropShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From b6c013ff6cd644586516157043059f098d522b82 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:20 +0000 Subject: [PATCH 245/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSQuadrilateralCropShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSQuadrilateralCropShader.json b/docs/_data/Help/Set-OBSQuadrilateralCropShader.json index e0609f0e..bf3337fe 100644 --- a/docs/_data/Help/Set-OBSQuadrilateralCropShader.json +++ b/docs/_data/Help/Set-OBSQuadrilateralCropShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSQuadrilateralCropShader [[-TopLeftX] ] [[-TopLeftY] ] [[-TopRightX] ] [[-TopRightY] ] [[-BottomLeftX] ] [[-BottomLeftY] ] [[-BottomRightX] ] [[-BottomRightY] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From da4686bd95a7138f6c28f87916f065f98d5a4d81 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:25 +0000 Subject: [PATCH 246/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/Set-OBSZoomBlurTransitionShader.md | 41 ++++++++++--------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/docs/Set-OBSZoomBlurTransitionShader.md b/docs/Set-OBSZoomBlurTransitionShader.md index 5859f433..b28ab22b 100644 --- a/docs/Set-OBSZoomBlurTransitionShader.md +++ b/docs/Set-OBSZoomBlurTransitionShader.md @@ -2,7 +2,6 @@ Get-OBSZoomBlurTransitionShader ------------------------------- ### Synopsis - Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [] --- @@ -16,73 +15,73 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-Tra |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|--------------| -|`[switch]`|false |Named |false |convert_linear| +|`[Switch]`|false |named |False |convert_linear| #### **FilterName** -|Type |Required|Position|PipelineInput | -|----------|--------|--------|---------------------| -|`[string]`|false |5 |true (ByPropertyName)| +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |False | #### **Force** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ImageA** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |0 |false |image_a| +|`[String]`|false |named |False |image_a| #### **ImageB** |Type |Required|Position|PipelineInput|Aliases| |----------|--------|--------|-------------|-------| -|`[string]`|false |1 |false |image_b| +|`[String]`|false |named |False |image_b| #### **NoResponse** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **PassThru** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | #### **ShaderText** |Type |Required|Position|PipelineInput|Aliases | |----------|--------|--------|-------------|-------------| -|`[string]`|false |6 |false |ShaderContent| +|`[String]`|false |named |False |ShaderContent| #### **SourceName** -|Type |Required|Position|PipelineInput |Aliases | -|----------|--------|--------|---------------------|-------------| -|`[string]`|false |4 |true (ByPropertyName)|SceneItemName| +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-------------| +|`[String]`|false |named |False |SceneItemName| #### **Strength** |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[float]`|false |3 |false | +|`[Float]`|false |named |False | #### **TransitionTime** |Type |Required|Position|PipelineInput|Aliases | |---------|--------|--------|-------------|---------------| -|`[float]`|false |2 |false |transition_time| +|`[Float]`|false |named |False |transition_time| #### **UseShaderTime** |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[switch]`|false |Named |false | +|`[Switch]`|false |named |False | --- @@ -98,11 +97,5 @@ System.String ### Syntax ```PowerShell -syntaxItem -``` -```PowerShell ----------- -``` -```PowerShell -{@{name=Get-OBSZoomBlurTransitionShader; CommonParameters=True; parameter=System.Object[]}} +Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [-ConvertLinear ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-Force ] [-PassThru ] [-NoResponse ] [-UseShaderTime ] [] ``` From d79e7a8825519a4840264d3f1d0fb8d7122da604 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:25 +0000 Subject: [PATCH 247/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/_data/Help/Set-OBSZoomBlurTransitionShader.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json b/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json index c14ca126..a23dbcaf 100644 --- a/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json +++ b/docs/_data/Help/Set-OBSZoomBlurTransitionShader.json @@ -1,5 +1,5 @@ { - "Synopsis": "\nGet-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []\n", + "Synopsis": "Get-OBSZoomBlurTransitionShader [[-ImageA] ] [[-ImageB] ] [[-TransitionTime] ] [[-Strength] ] [[-SourceName] ] [[-FilterName] ] [[-ShaderText] ] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] []", "Description": "", "Parameters": [ { @@ -23,7 +23,7 @@ null ], "Inputs": [ - "System.String\n" + "System.String" ], "Outputs": [ "System.Object" From 72279689ab995d4c0d10bf53d9e5f2fd1234c2c5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:21:57 +0000 Subject: [PATCH 248/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- docs/obs-powershell-commands.md | 1324 ++++++++++++++++--------------- 1 file changed, 672 insertions(+), 652 deletions(-) diff --git a/docs/obs-powershell-commands.md b/docs/obs-powershell-commands.md index 3d678294..4c2f15e3 100644 --- a/docs/obs-powershell-commands.md +++ b/docs/obs-powershell-commands.md @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 808 commands -(323 functions and 485 aliases) +obs-powershell exports 838 commands +(333 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](obs-powershell-websocket-commands.md). @@ -12,331 +12,341 @@ Functions ========= -|Name |Synopsis | -|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| -|[Add-OBSInput](Add-OBSInput.md) |Add-OBSInput : CreateInput | -|[Add-OBSProfile](Add-OBSProfile.md) |Add-OBSProfile : CreateProfile | -|[Add-OBSScene](Add-OBSScene.md) |Add-OBSScene : CreateScene | -|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) |Add-OBSSceneCollection : CreateSceneCollection | -|[Add-OBSSceneItem](Add-OBSSceneItem.md) |Add-OBSSceneItem : CreateSceneItem | -|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) |Add-OBSSourceFilter : CreateSourceFilter | -|[Clear-OBSScene](Clear-OBSScene.md) |Clears a Scene in OBS | -|[Connect-OBS](Connect-OBS.md) |Connects to Open Broadcast Studio | -|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) |Copy-OBSSceneItem : DuplicateSceneItem | -|[Disconnect-OBS](Disconnect-OBS.md) |Disconnects OBS | -|[Get-OBS](Get-OBS.md) |Gets OBS | -|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | -|[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | -|[Get-OBSAddShader](Get-OBSAddShader.md) | -|[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | -|[Get-OBSAlphaGamingBentCameraShader](Get-OBSAlphaGamingBentCameraShader.md) | -|[Get-OBSAnimatedPathShader](Get-OBSAnimatedPathShader.md) | -|[Get-OBSAnimatedTextureShader](Get-OBSAnimatedTextureShader.md) | -|[Get-OBSAsciiShader](Get-OBSAsciiShader.md) | -|[Get-OBSAspectRatioShader](Get-OBSAspectRatioShader.md) | -|[Get-OBSBackgroundRemovalShader](Get-OBSBackgroundRemovalShader.md) | -|[Get-OBSBlendOpacityShader](Get-OBSBlendOpacityShader.md) | -|[Get-OBSBlinkShader](Get-OBSBlinkShader.md) | -|[Get-OBSBloomShader](Get-OBSBloomShader.md) | -|[Get-OBSBorderShader](Get-OBSBorderShader.md) | -|[Get-OBSBoxBlurShader](Get-OBSBoxBlurShader.md) | -|[Get-OBSBulgePinchShader](Get-OBSBulgePinchShader.md) | -|[Get-OBSBurnShader](Get-OBSBurnShader.md) | -|[Get-OBSCartoonShader](Get-OBSCartoonShader.md) | -|[Get-OBSCellShadedShader](Get-OBSCellShadedShader.md) | -|[Get-OBSChromaticAberrationShader](Get-OBSChromaticAberrationShader.md) | -|[Get-OBSChromaUVDistortionShader](Get-OBSChromaUVDistortionShader.md) | -|[Get-OBSCircleMaskFilterShader](Get-OBSCircleMaskFilterShader.md) | -|[Get-OBSClockAnalogShader](Get-OBSClockAnalogShader.md) | -|[Get-OBSClockDigitalLedShader](Get-OBSClockDigitalLedShader.md) | -|[Get-OBSClockDigitalNixieShader](Get-OBSClockDigitalNixieShader.md) | -|[Get-OBSColorDepthShader](Get-OBSColorDepthShader.md) | -|[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | -|[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | -|[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | -|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) |Get-OBSCurrentPreviewScene : GetCurrentPreviewScene | -|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) |Get-OBSCurrentProgramScene : GetCurrentProgramScene | -|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) |Get-OBSCurrentSceneTransition : GetCurrentSceneTransition | -|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) |Get-OBSCurrentSceneTransitionCursor : GetCurrentSceneTransitionCursor | -|[Get-OBSCurveShader](Get-OBSCurveShader.md) | -|[Get-OBSCutRectPerCornerShader](Get-OBSCutRectPerCornerShader.md) | -|[Get-OBSCylinderShader](Get-OBSCylinderShader.md) | -|[Get-OBSDarkenShader](Get-OBSDarkenShader.md) | -|[Get-OBSDeadPixelFixerShader](Get-OBSDeadPixelFixerShader.md) | -|[Get-OBSDensitySatHueShader](Get-OBSDensitySatHueShader.md) | -|[Get-OBSDiffuseTransitionShader](Get-OBSDiffuseTransitionShader.md) | -|[Get-OBSDigitalRainShader](Get-OBSDigitalRainShader.md) | -|[Get-OBSDivideRotateShader](Get-OBSDivideRotateShader.md) | -|[Get-OBSDoodleShader](Get-OBSDoodleShader.md) | -|[Get-OBSDrawingsShader](Get-OBSDrawingsShader.md) | -|[Get-OBSDropShadowShader](Get-OBSDropShadowShader.md) | -|[Get-OBSDrunkShader](Get-OBSDrunkShader.md) | -|[Get-OBSDynamicMaskShader](Get-OBSDynamicMaskShader.md) | -|[Get-OBSEdgeDetectionShader](Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](Get-OBSEffect.md) |Gets OBS Effects | -|[Get-OBSEmbersShader](Get-OBSEmbersShader.md) | -|[Get-OBSEmbossColorShader](Get-OBSEmbossColorShader.md) | -|[Get-OBSEmbossShader](Get-OBSEmbossShader.md) | -|[Get-OBSExeldroBentCameraShader](Get-OBSExeldroBentCameraShader.md) | -|[Get-OBSFadeTransitionShader](Get-OBSFadeTransitionShader.md) | -|[Get-OBSFillColorGradientShader](Get-OBSFillColorGradientShader.md) | -|[Get-OBSFillColorLinearShader](Get-OBSFillColorLinearShader.md) | -|[Get-OBSFillColorRadialDegreesShader](Get-OBSFillColorRadialDegreesShader.md) | -|[Get-OBSFillColorRadialPercentageShader](Get-OBSFillColorRadialPercentageShader.md) | -|[Get-OBSFilterTemplateShader](Get-OBSFilterTemplateShader.md) | -|[Get-OBSFire3Shader](Get-OBSFire3Shader.md) | -|[Get-OBSFireShader](Get-OBSFireShader.md) | -|[Get-OBSFireworks2Shader](Get-OBSFireworks2Shader.md) | -|[Get-OBSFireworksShader](Get-OBSFireworksShader.md) | -|[Get-OBSFisheyeShader](Get-OBSFisheyeShader.md) | -|[Get-OBSFisheyeXyShader](Get-OBSFisheyeXyShader.md) | -|[Get-OBSFlipShader](Get-OBSFlipShader.md) | -|[Get-OBSFrostedGlassShader](Get-OBSFrostedGlassShader.md) | -|[Get-OBSGammaCorrectionShader](Get-OBSGammaCorrectionShader.md) | -|[Get-OBSGaussianBlurAdvancedShader](Get-OBSGaussianBlurAdvancedShader.md) | -|[Get-OBSGaussianBlurShader](Get-OBSGaussianBlurShader.md) | -|[Get-OBSGaussianBlurSimpleShader](Get-OBSGaussianBlurSimpleShader.md) | -|[Get-OBSGaussianExampleShader](Get-OBSGaussianExampleShader.md) | -|[Get-OBSGaussianSimpleShader](Get-OBSGaussianSimpleShader.md) | -|[Get-OBSGbCameraShader](Get-OBSGbCameraShader.md) | -|[Get-OBSGlassShader](Get-OBSGlassShader.md) | -|[Get-OBSGlitchAnalogShader](Get-OBSGlitchAnalogShader.md) | -|[Get-OBSGlitchShader](Get-OBSGlitchShader.md) | -|[Get-OBSGlowShader](Get-OBSGlowShader.md) | -|[Get-OBSGradientShader](Get-OBSGradientShader.md) | -|[Get-OBSGroup](Get-OBSGroup.md) |Get-OBSGroup : GetGroupList | -|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) |Get-OBSGroupSceneItem : GetGroupSceneItemList | -|[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | -|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | -|[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | -|[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](Get-OBSHotkey.md) |Get-OBSHotkey : GetHotkeyList | -|[Get-OBSHslHsvSaturationShader](Get-OBSHslHsvSaturationShader.md) | -|[Get-OBSHueRotatonShader](Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](Get-OBSInput.md) |Get-OBSInput : GetInputList | -|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) |Get-OBSInputAudioBalance : GetInputAudioBalance | -|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) |Get-OBSInputAudioMonitorType : GetInputAudioMonitorType | -|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) |Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset | -|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) |Get-OBSInputAudioTracks : GetInputAudioTracks | -|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) |Get-OBSInputDefaultSettings : GetInputDefaultSettings | -|[Get-OBSInputKind](Get-OBSInputKind.md) |Get-OBSInputKind : GetInputKindList | -|[Get-OBSInputMute](Get-OBSInputMute.md) |Get-OBSInputMute : GetInputMute | -|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md)|Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems| -|[Get-OBSInputSettings](Get-OBSInputSettings.md) |Get-OBSInputSettings : GetInputSettings | -|[Get-OBSInputVolume](Get-OBSInputVolume.md) |Get-OBSInputVolume : GetInputVolume | -|[Get-OBSIntensityScopeShader](Get-OBSIntensityScopeShader.md) | -|[Get-OBSInvertLumaShader](Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) |Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay | -|[Get-OBSLuminance2Shader](Get-OBSLuminance2Shader.md) | -|[Get-OBSLuminanceAlphaShader](Get-OBSLuminanceAlphaShader.md) | -|[Get-OBSLuminanceShader](Get-OBSLuminanceShader.md) | -|[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) |Get-OBSMediaInputStatus : GetMediaInputStatus | -|[Get-OBSMonitor](Get-OBSMonitor.md) |Get-OBSMonitor : GetMonitorList | -|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | -|[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | -|[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | -|[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | -|[Get-OBSOutput](Get-OBSOutput.md) |Get-OBSOutput : GetOutputList | -|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) |Get-OBSOutputSettings : GetOutputSettings | -|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) |Get-OBSOutputStatus : GetOutputStatus | -|[Get-OBSPagePeelShader](Get-OBSPagePeelShader.md) | -|[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | -|[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](Get-OBSPersistentData.md) |Get-OBSPersistentData : GetPersistentData | -|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | -|[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | -|[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | -|[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | -|[Get-OBSPolarShader](Get-OBSPolarShader.md) | -|[Get-OBSProfile](Get-OBSProfile.md) |Get-OBSProfile : GetProfileList | -|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) |Get-OBSProfileParameter : GetProfileParameter | -|[Get-OBSPulseShader](Get-OBSPulseShader.md) | -|[Get-OBSRainbowShader](Get-OBSRainbowShader.md) | -|[Get-OBSRainWindowShader](Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) |Get-OBSRecordDirectory : GetRecordDirectory | -|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) |Get-OBSRecordStatus : GetRecordStatus | -|[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | -|[Get-OBSReflectShader](Get-OBSReflectShader.md) | -|[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | -|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | -|[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | -|[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) |Get-OBSReplayBufferStatus : GetReplayBufferStatus | -|[Get-OBSRGBAPercentShader](Get-OBSRGBAPercentShader.md) | -|[Get-OBSRgbColorWheelShader](Get-OBSRgbColorWheelShader.md) | -|[Get-OBSRgbSplitShader](Get-OBSRgbSplitShader.md) | -|[Get-OBSRgbvisibilityShader](Get-OBSRgbvisibilityShader.md) | -|[Get-OBSRGSSAAShader](Get-OBSRGSSAAShader.md) | -|[Get-OBSRippleShader](Get-OBSRippleShader.md) | -|[Get-OBSRotatingSourceShader](Get-OBSRotatingSourceShader.md) | -|[Get-OBSRotatoeShader](Get-OBSRotatoeShader.md) | -|[Get-OBSRoundedRect2Shader](Get-OBSRoundedRect2Shader.md) | -|[Get-OBSRoundedRectPerCornerShader](Get-OBSRoundedRectPerCornerShader.md) | -|[Get-OBSRoundedRectPerSideShader](Get-OBSRoundedRectPerSideShader.md) | -|[Get-OBSRoundedRectShader](Get-OBSRoundedRectShader.md) | -|[Get-OBSRoundedStrokeGradientShader](Get-OBSRoundedStrokeGradientShader.md) | -|[Get-OBSRoundedStrokeShader](Get-OBSRoundedStrokeShader.md) | -|[Get-OBSScanLineShader](Get-OBSScanLineShader.md) | -|[Get-OBSScene](Get-OBSScene.md) |Get-OBSScene : GetSceneList | -|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) |Get-OBSSceneCollection : GetSceneCollectionList | -|[Get-OBSSceneItem](Get-OBSSceneItem.md) |Get-OBSSceneItem : GetSceneItemList | -|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) |Get-OBSSceneItemBlendMode : GetSceneItemBlendMode | -|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) |Get-OBSSceneItemEnabled : GetSceneItemEnabled | -|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) |Get-OBSSceneItemId : GetSceneItemId | -|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) |Get-OBSSceneItemIndex : GetSceneItemIndex | -|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) |Get-OBSSceneItemLocked : GetSceneItemLocked | -|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) |Get-OBSSceneItemSource : GetSceneItemSource | -|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) |Get-OBSSceneItemTransform : GetSceneItemTransform | -|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) |Get-OBSSceneSceneTransitionOverride : GetSceneSceneTransitionOverride | -|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) |Get-OBSSceneTransition : GetSceneTransitionList | -|[Get-OBSSeascapeShader](Get-OBSSeascapeShader.md) | -|[Get-OBSSeasickShader](Get-OBSSeasickShader.md) | -|[Get-OBSSelectiveColorShader](Get-OBSSelectiveColorShader.md) | -|[Get-OBSShakeShader](Get-OBSShakeShader.md) | -|[Get-OBSShineShader](Get-OBSShineShader.md) | -|[Get-OBSSimpleGradientShader](Get-OBSSimpleGradientShader.md) | -|[Get-OBSSimplexNoiseShader](Get-OBSSimplexNoiseShader.md) | -|[Get-OBSSmartDenoiseShader](Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](Get-OBSSourceActive.md) |Get-OBSSourceActive : GetSourceActive | -|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) |Get-OBSSourceFilter : GetSourceFilter | -|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) |Get-OBSSourceFilterDefaultSettings : GetSourceFilterDefaultSettings | -|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) |Get-OBSSourceFilterKind : GetSourceFilterKindList | -|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) |Get-OBSSourceFilterList : GetSourceFilterList | -|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) |Get-OBSSourceScreenshot : GetSourceScreenshot | -|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) |Get-OBSSpecialInputs : GetSpecialInputs | -|[Get-OBSSpecularShineShader](Get-OBSSpecularShineShader.md) | -|[Get-OBSSpotlightShader](Get-OBSSpotlightShader.md) | -|[Get-OBSStats](Get-OBSStats.md) |Get-OBSStats : GetStats | -|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) |Get-OBSStreamServiceSettings : GetStreamServiceSettings | -|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) |Get-OBSStreamStatus : GetStreamStatus | -|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) |Get-OBSStudioModeEnabled : GetStudioModeEnabled | -|[Get-OBSSwirlShader](Get-OBSSwirlShader.md) | -|[Get-OBSTetraShader](Get-OBSTetraShader.md) | -|[Get-OBSThermalShader](Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) |Get-OBSTransitionKind : GetTransitionKindList | -|[Get-OBSTvCrtSubpixelShader](Get-OBSTvCrtSubpixelShader.md) | -|[Get-OBSTwistShader](Get-OBSTwistShader.md) | -|[Get-OBSTwoPassDropShadowShader](Get-OBSTwoPassDropShadowShader.md) | -|[Get-OBSVCRShader](Get-OBSVCRShader.md) | -|[Get-OBSVersion](Get-OBSVersion.md) |Get-OBSVersion : GetVersion | -|[Get-OBSVHSShader](Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) |Get-OBSVideoSettings : GetVideoSettings | -|[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) |Get-OBSVirtualCamStatus : GetVirtualCamStatus | -|[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | -|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | -|[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | -|[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | -|[Get-OBSZoomShader](Get-OBSZoomShader.md) | -|[Get-OBSZoomXYShader](Get-OBSZoomXYShader.md) | -|[Hide-OBS](Hide-OBS.md) |Hide OBS | -|[Import-OBSEffect](Import-OBSEffect.md) |Imports Effects | -|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) |Open-OBSInputFiltersDialog : OpenInputFiltersDialog | -|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) |Open-OBSInputInteractDialog : OpenInputInteractDialog | -|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) |Open-OBSInputPropertiesDialog : OpenInputPropertiesDialog | -|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) |Open-OBSSourceProjector : OpenSourceProjector | -|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) |Open-OBSVideoMixProjector : OpenVideoMixProjector | -|[Receive-OBS](Receive-OBS.md) |Receives data from OBS | -|[Remove-OBS](Remove-OBS.md) |Remove OBS | -|[Remove-OBSEffect](Remove-OBSEffect.md) |Removes OBS Effects | -|[Remove-OBSInput](Remove-OBSInput.md) |Remove-OBSInput : RemoveInput | -|[Remove-OBSProfile](Remove-OBSProfile.md) |Remove-OBSProfile : RemoveProfile | -|[Remove-OBSScene](Remove-OBSScene.md) |Remove-OBSScene : RemoveScene | -|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) |Remove-OBSSceneItem : RemoveSceneItem | -|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) |Remove-OBSSourceFilter : RemoveSourceFilter | -|[Resume-OBSRecord](Resume-OBSRecord.md) |Resume-OBSRecord : ResumeRecord | -|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) |Save-OBSReplayBuffer : SaveReplayBuffer | -|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) |Save-OBSSourceScreenshot : SaveSourceScreenshot | -|[Send-OBS](Send-OBS.md) |Sends messages to the OBS websocket. | -|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) |Send-OBSCallVendorRequest : CallVendorRequest | -|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) |Send-OBSCustomEvent : BroadcastCustomEvent | -|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) |Send-OBSOffsetMediaInputCursor : OffsetMediaInputCursor | -|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) |Send-OBSPauseRecord : PauseRecord | -|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) |Send-OBSPressInputPropertiesButton : PressInputPropertiesButton | -|[Send-OBSSleep](Send-OBSSleep.md) |Send-OBSSleep : Sleep | -|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) |Send-OBSStreamCaption : SendStreamCaption | -|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) |Send-OBSTriggerHotkeyByKeySequence : TriggerHotkeyByKeySequence | -|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) |Send-OBSTriggerHotkeyByName : TriggerHotkeyByName | -|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) |Send-OBSTriggerMediaInputAction : TriggerMediaInputAction | -|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) |Send-OBSTriggerStudioModeTransition : TriggerStudioModeTransition | -|[Set-OBS3DFilter](Set-OBS3DFilter.md) |Sets an OBS 3D Filter. | -|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) |Adds or sets an audio output source | -|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) |Sets a browser source | -|[Set-OBSColorFilter](Set-OBSColorFilter.md) |Sets a color filter | -|[Set-OBSColorSource](Set-OBSColorSource.md) |Adds a color source | -|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) |Set-OBSCurrentPreviewScene : SetCurrentPreviewScene | -|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) |Set-OBSCurrentProfile : SetCurrentProfile | -|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) |Set-OBSCurrentProgramScene : SetCurrentProgramScene | -|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) |Set-OBSCurrentSceneCollection : SetCurrentSceneCollection | -|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) |Set-OBSCurrentSceneTransition : SetCurrentSceneTransition | -|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) |Set-OBSCurrentSceneTransitionDuration : SetCurrentSceneTransitionDuration | -|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) |Set-OBSCurrentSceneTransitionSettings : SetCurrentSceneTransitionSettings | -|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) |Adds a display source | -|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) |Sets a Equalizer filter. | -|[Set-OBSGainFilter](Set-OBSGainFilter.md) |Sets a Gain filter. | -|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) |Set-OBSInputAudioBalance : SetInputAudioBalance | -|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) |Set-OBSInputAudioMonitorType : SetInputAudioMonitorType | -|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) |Set-OBSInputAudioSyncOffset : SetInputAudioSyncOffset | -|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) |Set-OBSInputAudioTracks : SetInputAudioTracks | -|[Set-OBSInputMute](Set-OBSInputMute.md) |Set-OBSInputMute : SetInputMute | -|[Set-OBSInputName](Set-OBSInputName.md) |Set-OBSInputName : SetInputName | -|[Set-OBSInputSettings](Set-OBSInputSettings.md) |Set-OBSInputSettings : SetInputSettings | -|[Set-OBSInputVolume](Set-OBSInputVolume.md) |Set-OBSInputVolume : SetInputVolume | -|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) |Sets a markdown source | -|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) |Set-OBSMediaInputCursor : SetMediaInputCursor | -|[Set-OBSMediaSource](Set-OBSMediaSource.md) |Adds a media source | -|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) |Set-OBSOutputSettings : SetOutputSettings | -|[Set-OBSPersistentData](Set-OBSPersistentData.md) |Set-OBSPersistentData : SetPersistentData | -|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) |Set-OBSProfileParameter : SetProfileParameter | -|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) |Set-OBSRecordDirectory : SetRecordDirectory | -|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) |Sets a RenderDelay filter. | -|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) |Sets a Scale filter. | -|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) |Set-OBSSceneItemBlendMode : SetSceneItemBlendMode | -|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) |Set-OBSSceneItemEnabled : SetSceneItemEnabled | -|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) |Set-OBSSceneItemIndex : SetSceneItemIndex | -|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) |Set-OBSSceneItemLocked : SetSceneItemLocked | -|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) |Set-OBSSceneItemTransform : SetSceneItemTransform | -|[Set-OBSSceneName](Set-OBSSceneName.md) |Set-OBSSceneName : SetSceneName | -|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) |Set-OBSSceneSceneTransitionOverride : SetSceneSceneTransitionOverride | -|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) |Sets a scroll filter. | -|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) |Sets a Shader filter. | -|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) |Sets a Sharpness filter. | -|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) |Sets a Sound Cloud Source | -|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) |Set-OBSSourceFilterEnabled : SetSourceFilterEnabled | -|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) |Set-OBSSourceFilterIndex : SetSourceFilterIndex | -|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) |Set-OBSSourceFilterName : SetSourceFilterName | -|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) |Set-OBSSourceFilterSettings : SetSourceFilterSettings | -|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) |Set-OBSStreamServiceSettings : SetStreamServiceSettings | -|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) |Set-OBSStudioModeEnabled : SetStudioModeEnabled | -|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) |Adds a VLC playlist source | -|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) |Set-OBSTBarPosition : SetTBarPosition | -|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) |Set-OBSVideoSettings : SetVideoSettings | -|[Set-OBSVLCSource](Set-OBSVLCSource.md) |Adds a VLC playlist source | -|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) |OBS Waveform Source | -|[Set-OBSWindowSource](Set-OBSWindowSource.md) |Adds or sets a window capture source | -|[Show-OBS](Show-OBS.md) |Shows content in OBS | -|[Start-OBSEffect](Start-OBSEffect.md) |Starts obs-powershell effects. | -|[Start-OBSOutput](Start-OBSOutput.md) |Start-OBSOutput : StartOutput | -|[Start-OBSRecord](Start-OBSRecord.md) |Start-OBSRecord : StartRecord | -|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) |Start-OBSReplayBuffer : StartReplayBuffer | -|[Start-OBSStream](Start-OBSStream.md) |Start-OBSStream : StartStream | -|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) |Start-OBSVirtualCam : StartVirtualCam | -|[Stop-OBSEffect](Stop-OBSEffect.md) |Stops obs-powershell effects. | -|[Stop-OBSOutput](Stop-OBSOutput.md) |Stop-OBSOutput : StopOutput | -|[Stop-OBSRecord](Stop-OBSRecord.md) |Stop-OBSRecord : StopRecord | -|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) |Stop-OBSReplayBuffer : StopReplayBuffer | -|[Stop-OBSStream](Stop-OBSStream.md) |Stop-OBSStream : StopStream | -|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) |Stop-OBSVirtualCam : StopVirtualCam | -|[Switch-OBSInputMute](Switch-OBSInputMute.md) |Switch-OBSInputMute : ToggleInputMute | -|[Switch-OBSOutput](Switch-OBSOutput.md) |Switch-OBSOutput : ToggleOutput | -|[Switch-OBSRecord](Switch-OBSRecord.md) |Switch-OBSRecord : ToggleRecord | -|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) |Switch-OBSRecordPause : ToggleRecordPause | -|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) |Switch-OBSReplayBuffer : ToggleReplayBuffer | -|[Switch-OBSStream](Switch-OBSStream.md) |Switch-OBSStream : ToggleStream | -|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) |Switch-OBSVirtualCam : ToggleVirtualCam | -|[Watch-OBS](Watch-OBS.md) |Watches OBS | +|Name |Synopsis| +|------------------------------------------------------------------------------------------------|--------| +|[Add-OBSInput](Add-OBSInput.md) | +|[Add-OBSProfile](Add-OBSProfile.md) | +|[Add-OBSScene](Add-OBSScene.md) | +|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) | +|[Add-OBSSceneItem](Add-OBSSceneItem.md) | +|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) | +|[Clear-OBSScene](Clear-OBSScene.md) | +|[Connect-OBS](Connect-OBS.md) | +|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) | +|[Disconnect-OBS](Disconnect-OBS.md) | +|[Get-OBS](Get-OBS.md) | +|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | +|[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | +|[Get-OBSAddShader](Get-OBSAddShader.md) | +|[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | +|[Get-OBSAlphaGamingBentCameraShader](Get-OBSAlphaGamingBentCameraShader.md) | +|[Get-OBSAnimatedPathShader](Get-OBSAnimatedPathShader.md) | +|[Get-OBSAnimatedTextureShader](Get-OBSAnimatedTextureShader.md) | +|[Get-OBSAsciiShader](Get-OBSAsciiShader.md) | +|[Get-OBSAspectRatioShader](Get-OBSAspectRatioShader.md) | +|[Get-OBSAudioShader](Get-OBSAudioShader.md) | +|[Get-OBSBackgroundRemovalShader](Get-OBSBackgroundRemovalShader.md) | +|[Get-OBSBlendOpacityShader](Get-OBSBlendOpacityShader.md) | +|[Get-OBSBlinkShader](Get-OBSBlinkShader.md) | +|[Get-OBSBloomShader](Get-OBSBloomShader.md) | +|[Get-OBSBorderShader](Get-OBSBorderShader.md) | +|[Get-OBSBoxBlurShader](Get-OBSBoxBlurShader.md) | +|[Get-OBSBulgePinchShader](Get-OBSBulgePinchShader.md) | +|[Get-OBSBurnShader](Get-OBSBurnShader.md) | +|[Get-OBSCartoonShader](Get-OBSCartoonShader.md) | +|[Get-OBSCellShadedShader](Get-OBSCellShadedShader.md) | +|[Get-OBSChromaticAberrationShader](Get-OBSChromaticAberrationShader.md) | +|[Get-OBSChromaUVDistortionShader](Get-OBSChromaUVDistortionShader.md) | +|[Get-OBSCircleMaskFilterShader](Get-OBSCircleMaskFilterShader.md) | +|[Get-OBSClockAnalogShader](Get-OBSClockAnalogShader.md) | +|[Get-OBSClockDigitalLedShader](Get-OBSClockDigitalLedShader.md) | +|[Get-OBSClockDigitalNixieShader](Get-OBSClockDigitalNixieShader.md) | +|[Get-OBSColorDepthShader](Get-OBSColorDepthShader.md) | +|[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | +|[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | +|[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | +|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) | +|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) | +|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) | +|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurveShader](Get-OBSCurveShader.md) | +|[Get-OBSCutRectPerCornerShader](Get-OBSCutRectPerCornerShader.md) | +|[Get-OBSCylinderShader](Get-OBSCylinderShader.md) | +|[Get-OBSDarkenShader](Get-OBSDarkenShader.md) | +|[Get-OBSDeadPixelFixerShader](Get-OBSDeadPixelFixerShader.md) | +|[Get-OBSDensitySatHueShader](Get-OBSDensitySatHueShader.md) | +|[Get-OBSDiffuseTransitionShader](Get-OBSDiffuseTransitionShader.md) | +|[Get-OBSDigitalRainShader](Get-OBSDigitalRainShader.md) | +|[Get-OBSDisplacementMapAdvancedInvertShader](Get-OBSDisplacementMapAdvancedInvertShader.md)| +|[Get-OBSDisplacementMapAdvancedShader](Get-OBSDisplacementMapAdvancedShader.md) | +|[Get-OBSDisplacementMapInvertShader](Get-OBSDisplacementMapInvertShader.md) | +|[Get-OBSDisplacementMapShader](Get-OBSDisplacementMapShader.md) | +|[Get-OBSDivideRotateShader](Get-OBSDivideRotateShader.md) | +|[Get-OBSDoodleShader](Get-OBSDoodleShader.md) | +|[Get-OBSDrawingsShader](Get-OBSDrawingsShader.md) | +|[Get-OBSDropShadowShader](Get-OBSDropShadowShader.md) | +|[Get-OBSDrunkShader](Get-OBSDrunkShader.md) | +|[Get-OBSDynamicMaskShader](Get-OBSDynamicMaskShader.md) | +|[Get-OBSEdgeDetectionShader](Get-OBSEdgeDetectionShader.md) | +|[Get-OBSEffect](Get-OBSEffect.md) | +|[Get-OBSEmbersShader](Get-OBSEmbersShader.md) | +|[Get-OBSEmbossColorShader](Get-OBSEmbossColorShader.md) | +|[Get-OBSEmbossShader](Get-OBSEmbossShader.md) | +|[Get-OBSExeldroBentCameraShader](Get-OBSExeldroBentCameraShader.md) | +|[Get-OBSFadeTransitionShader](Get-OBSFadeTransitionShader.md) | +|[Get-OBSFillColorGradientShader](Get-OBSFillColorGradientShader.md) | +|[Get-OBSFillColorLinearShader](Get-OBSFillColorLinearShader.md) | +|[Get-OBSFillColorRadialDegreesShader](Get-OBSFillColorRadialDegreesShader.md) | +|[Get-OBSFillColorRadialPercentageShader](Get-OBSFillColorRadialPercentageShader.md) | +|[Get-OBSFilterTemplateShader](Get-OBSFilterTemplateShader.md) | +|[Get-OBSFire3Shader](Get-OBSFire3Shader.md) | +|[Get-OBSFireShader](Get-OBSFireShader.md) | +|[Get-OBSFireworks2Shader](Get-OBSFireworks2Shader.md) | +|[Get-OBSFireworksShader](Get-OBSFireworksShader.md) | +|[Get-OBSFisheyeShader](Get-OBSFisheyeShader.md) | +|[Get-OBSFisheyeXyShader](Get-OBSFisheyeXyShader.md) | +|[Get-OBSFlipShader](Get-OBSFlipShader.md) | +|[Get-OBSFrostedGlassShader](Get-OBSFrostedGlassShader.md) | +|[Get-OBSGammaCorrectionShader](Get-OBSGammaCorrectionShader.md) | +|[Get-OBSGaussianBlurAdvancedShader](Get-OBSGaussianBlurAdvancedShader.md) | +|[Get-OBSGaussianBlurShader](Get-OBSGaussianBlurShader.md) | +|[Get-OBSGaussianBlurSimpleShader](Get-OBSGaussianBlurSimpleShader.md) | +|[Get-OBSGaussianExampleShader](Get-OBSGaussianExampleShader.md) | +|[Get-OBSGaussianSimpleShader](Get-OBSGaussianSimpleShader.md) | +|[Get-OBSGbCameraShader](Get-OBSGbCameraShader.md) | +|[Get-OBSGlassShader](Get-OBSGlassShader.md) | +|[Get-OBSGlitchAnalogShader](Get-OBSGlitchAnalogShader.md) | +|[Get-OBSGlitchPeriodicShader](Get-OBSGlitchPeriodicShader.md) | +|[Get-OBSGlitchShader](Get-OBSGlitchShader.md) | +|[Get-OBSGlowShader](Get-OBSGlowShader.md) | +|[Get-OBSGradientShader](Get-OBSGradientShader.md) | +|[Get-OBSGroup](Get-OBSGroup.md) | +|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) | +|[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | +|[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | +|[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | +|[Get-OBSHotkey](Get-OBSHotkey.md) | +|[Get-OBSHslHsvSaturationShader](Get-OBSHslHsvSaturationShader.md) | +|[Get-OBSHueRotatonShader](Get-OBSHueRotatonShader.md) | +|[Get-OBSInput](Get-OBSInput.md) | +|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) | +|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) | +|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) | +|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) | +|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) | +|[Get-OBSInputKind](Get-OBSInputKind.md) | +|[Get-OBSInputMute](Get-OBSInputMute.md) | +|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md) | +|[Get-OBSInputSettings](Get-OBSInputSettings.md) | +|[Get-OBSInputVolume](Get-OBSInputVolume.md) | +|[Get-OBSIntensityScopeShader](Get-OBSIntensityScopeShader.md) | +|[Get-OBSInvertLumaShader](Get-OBSInvertLumaShader.md) | +|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLuminance2Shader](Get-OBSLuminance2Shader.md) | +|[Get-OBSLuminanceAlphaShader](Get-OBSLuminanceAlphaShader.md) | +|[Get-OBSLuminanceShader](Get-OBSLuminanceShader.md) | +|[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | +|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) | +|[Get-OBSMonitor](Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | +|[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | +|[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | +|[Get-OBSNoiseShader](Get-OBSNoiseShader.md) | +|[Get-OBSNormalMapShader](Get-OBSNormalMapShader.md) | +|[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | +|[Get-OBSOutput](Get-OBSOutput.md) | +|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) | +|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) | +|[Get-OBSPagePeelShader](Get-OBSPagePeelShader.md) | +|[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | +|[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | +|[Get-OBSPersistentData](Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | +|[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | +|[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | +|[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | +|[Get-OBSPolarShader](Get-OBSPolarShader.md) | +|[Get-OBSProfile](Get-OBSProfile.md) | +|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) | +|[Get-OBSPulseShader](Get-OBSPulseShader.md) | +|[Get-OBSQuadrilateralCropShader](Get-OBSQuadrilateralCropShader.md) | +|[Get-OBSRainbowShader](Get-OBSRainbowShader.md) | +|[Get-OBSRainWindowShader](Get-OBSRainWindowShader.md) | +|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) | +|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) | +|[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | +|[Get-OBSReflectShader](Get-OBSReflectShader.md) | +|[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | +|[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | +|[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | +|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) | +|[Get-OBSRGBAPercentShader](Get-OBSRGBAPercentShader.md) | +|[Get-OBSRgbColorWheelShader](Get-OBSRgbColorWheelShader.md) | +|[Get-OBSRgbSplitShader](Get-OBSRgbSplitShader.md) | +|[Get-OBSRgbvisibilityShader](Get-OBSRgbvisibilityShader.md) | +|[Get-OBSRGSSAAShader](Get-OBSRGSSAAShader.md) | +|[Get-OBSRippleShader](Get-OBSRippleShader.md) | +|[Get-OBSRotatingSourceShader](Get-OBSRotatingSourceShader.md) | +|[Get-OBSRotatoeShader](Get-OBSRotatoeShader.md) | +|[Get-OBSRoundedRect2Shader](Get-OBSRoundedRect2Shader.md) | +|[Get-OBSRoundedRectPerCornerShader](Get-OBSRoundedRectPerCornerShader.md) | +|[Get-OBSRoundedRectPerSideShader](Get-OBSRoundedRectPerSideShader.md) | +|[Get-OBSRoundedRectShader](Get-OBSRoundedRectShader.md) | +|[Get-OBSRoundedStrokeGradientShader](Get-OBSRoundedStrokeGradientShader.md) | +|[Get-OBSRoundedStrokeShader](Get-OBSRoundedStrokeShader.md) | +|[Get-OBSScanLineShader](Get-OBSScanLineShader.md) | +|[Get-OBSScene](Get-OBSScene.md) | +|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) | +|[Get-OBSSceneItem](Get-OBSSceneItem.md) | +|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) | +|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) | +|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) | +|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) | +|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) | +|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) | +|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) | +|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) | +|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) | +|[Get-OBSSeascapeShader](Get-OBSSeascapeShader.md) | +|[Get-OBSSeasickShader](Get-OBSSeasickShader.md) | +|[Get-OBSSelectiveColorShader](Get-OBSSelectiveColorShader.md) | +|[Get-OBSShakeShader](Get-OBSShakeShader.md) | +|[Get-OBSShineShader](Get-OBSShineShader.md) | +|[Get-OBSSimpleGradientShader](Get-OBSSimpleGradientShader.md) | +|[Get-OBSSimplexNoiseShader](Get-OBSSimplexNoiseShader.md) | +|[Get-OBSSmartDenoiseShader](Get-OBSSmartDenoiseShader.md) | +|[Get-OBSSourceActive](Get-OBSSourceActive.md) | +|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) | +|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) | +|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) | +|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) | +|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) | +|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) | +|[Get-OBSSpecularShineShader](Get-OBSSpecularShineShader.md) | +|[Get-OBSSpotlightShader](Get-OBSSpotlightShader.md) | +|[Get-OBSStats](Get-OBSStats.md) | +|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) | +|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) | +|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) | +|[Get-OBSSwirlShader](Get-OBSSwirlShader.md) | +|[Get-OBSTetraShader](Get-OBSTetraShader.md) | +|[Get-OBSThermalShader](Get-OBSThermalShader.md) | +|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) | +|[Get-OBSTvCrtSubpixelShader](Get-OBSTvCrtSubpixelShader.md) | +|[Get-OBSTwistShader](Get-OBSTwistShader.md) | +|[Get-OBSTwoPassDropShadowShader](Get-OBSTwoPassDropShadowShader.md) | +|[Get-OBSVCRShader](Get-OBSVCRShader.md) | +|[Get-OBSVersion](Get-OBSVersion.md) | +|[Get-OBSVHSShader](Get-OBSVHSShader.md) | +|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) | +|[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | +|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) | +|[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | +|[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | +|[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | +|[Get-OBSZoomBlurTransitionShader](Get-OBSZoomBlurTransitionShader.md) | +|[Get-OBSZoomShader](Get-OBSZoomShader.md) | +|[Get-OBSZoomXYShader](Get-OBSZoomXYShader.md) | +|[Hide-OBS](Hide-OBS.md) | +|[Import-OBSEffect](Import-OBSEffect.md) | +|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) | +|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) | +|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) | +|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) | +|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) | +|[Receive-OBS](Receive-OBS.md) | +|[Remove-OBS](Remove-OBS.md) | +|[Remove-OBSEffect](Remove-OBSEffect.md) | +|[Remove-OBSInput](Remove-OBSInput.md) | +|[Remove-OBSProfile](Remove-OBSProfile.md) | +|[Remove-OBSScene](Remove-OBSScene.md) | +|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) | +|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) | +|[Resume-OBSRecord](Resume-OBSRecord.md) | +|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) | +|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) | +|[Send-OBS](Send-OBS.md) | +|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) | +|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) | +|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) | +|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) | +|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) | +|[Send-OBSSleep](Send-OBSSleep.md) | +|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) | +|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) | +|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) | +|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) | +|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) | +|[Set-OBS3DFilter](Set-OBS3DFilter.md) | +|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) | +|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) | +|[Set-OBSColorFilter](Set-OBSColorFilter.md) | +|[Set-OBSColorSource](Set-OBSColorSource.md) | +|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) | +|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) | +|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) | +|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) | +|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) | +|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) | +|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) | +|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) | +|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) | +|[Set-OBSGainFilter](Set-OBSGainFilter.md) | +|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) | +|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) | +|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) | +|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) | +|[Set-OBSInputMute](Set-OBSInputMute.md) | +|[Set-OBSInputName](Set-OBSInputName.md) | +|[Set-OBSInputSettings](Set-OBSInputSettings.md) | +|[Set-OBSInputVolume](Set-OBSInputVolume.md) | +|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) | +|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) | +|[Set-OBSMediaSource](Set-OBSMediaSource.md) | +|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) | +|[Set-OBSPersistentData](Set-OBSPersistentData.md) | +|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) | +|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) | +|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) | +|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) | +|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) | +|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) | +|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) | +|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) | +|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) | +|[Set-OBSSceneName](Set-OBSSceneName.md) | +|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) | +|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) | +|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) | +|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) | +|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) | +|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) | +|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) | +|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) | +|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) | +|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) | +|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) | +|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) | +|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) | +|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) | +|[Set-OBSVLCSource](Set-OBSVLCSource.md) | +|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | +|[Set-OBSWindowSource](Set-OBSWindowSource.md) | +|[Show-OBS](Show-OBS.md) | +|[Start-OBSEffect](Start-OBSEffect.md) | +|[Start-OBSOutput](Start-OBSOutput.md) | +|[Start-OBSRecord](Start-OBSRecord.md) | +|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | +|[Start-OBSStream](Start-OBSStream.md) | +|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | +|[Stop-OBSEffect](Stop-OBSEffect.md) | +|[Stop-OBSOutput](Stop-OBSOutput.md) | +|[Stop-OBSRecord](Stop-OBSRecord.md) | +|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) | +|[Stop-OBSStream](Stop-OBSStream.md) | +|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) | +|[Switch-OBSInputMute](Switch-OBSInputMute.md) | +|[Switch-OBSOutput](Switch-OBSOutput.md) | +|[Switch-OBSRecord](Switch-OBSRecord.md) | +|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) | +|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) | +|[Switch-OBSStream](Switch-OBSStream.md) | +|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) | +|[Watch-OBS](Watch-OBS.md) | @@ -344,328 +354,338 @@ Functions Aliases ======= -|Name |ResolvedCommand| -|------------------------------------------------------------------------------------------|---------------| -|[Add-OBSInput](Add-OBSInput.md) | -|[Add-OBSProfile](Add-OBSProfile.md) | -|[Add-OBSScene](Add-OBSScene.md) | -|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) | -|[Add-OBSSceneItem](Add-OBSSceneItem.md) | -|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) | -|[Clear-OBSScene](Clear-OBSScene.md) | -|[Connect-OBS](Connect-OBS.md) | -|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) | -|[Disconnect-OBS](Disconnect-OBS.md) | -|[Get-OBS](Get-OBS.md) | -|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | -|[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | -|[Get-OBSAddShader](Get-OBSAddShader.md) | -|[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | -|[Get-OBSAlphaGamingBentCameraShader](Get-OBSAlphaGamingBentCameraShader.md) | -|[Get-OBSAnimatedPathShader](Get-OBSAnimatedPathShader.md) | -|[Get-OBSAnimatedTextureShader](Get-OBSAnimatedTextureShader.md) | -|[Get-OBSAsciiShader](Get-OBSAsciiShader.md) | -|[Get-OBSAspectRatioShader](Get-OBSAspectRatioShader.md) | -|[Get-OBSBackgroundRemovalShader](Get-OBSBackgroundRemovalShader.md) | -|[Get-OBSBlendOpacityShader](Get-OBSBlendOpacityShader.md) | -|[Get-OBSBlinkShader](Get-OBSBlinkShader.md) | -|[Get-OBSBloomShader](Get-OBSBloomShader.md) | -|[Get-OBSBorderShader](Get-OBSBorderShader.md) | -|[Get-OBSBoxBlurShader](Get-OBSBoxBlurShader.md) | -|[Get-OBSBulgePinchShader](Get-OBSBulgePinchShader.md) | -|[Get-OBSBurnShader](Get-OBSBurnShader.md) | -|[Get-OBSCartoonShader](Get-OBSCartoonShader.md) | -|[Get-OBSCellShadedShader](Get-OBSCellShadedShader.md) | -|[Get-OBSChromaticAberrationShader](Get-OBSChromaticAberrationShader.md) | -|[Get-OBSChromaUVDistortionShader](Get-OBSChromaUVDistortionShader.md) | -|[Get-OBSCircleMaskFilterShader](Get-OBSCircleMaskFilterShader.md) | -|[Get-OBSClockAnalogShader](Get-OBSClockAnalogShader.md) | -|[Get-OBSClockDigitalLedShader](Get-OBSClockDigitalLedShader.md) | -|[Get-OBSClockDigitalNixieShader](Get-OBSClockDigitalNixieShader.md) | -|[Get-OBSColorDepthShader](Get-OBSColorDepthShader.md) | -|[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | -|[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | -|[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | -|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | -|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) | -|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) | -|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) | -|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) | -|[Get-OBSCurveShader](Get-OBSCurveShader.md) | -|[Get-OBSCutRectPerCornerShader](Get-OBSCutRectPerCornerShader.md) | -|[Get-OBSCylinderShader](Get-OBSCylinderShader.md) | -|[Get-OBSDarkenShader](Get-OBSDarkenShader.md) | -|[Get-OBSDeadPixelFixerShader](Get-OBSDeadPixelFixerShader.md) | -|[Get-OBSDensitySatHueShader](Get-OBSDensitySatHueShader.md) | -|[Get-OBSDiffuseTransitionShader](Get-OBSDiffuseTransitionShader.md) | -|[Get-OBSDigitalRainShader](Get-OBSDigitalRainShader.md) | -|[Get-OBSDivideRotateShader](Get-OBSDivideRotateShader.md) | -|[Get-OBSDoodleShader](Get-OBSDoodleShader.md) | -|[Get-OBSDrawingsShader](Get-OBSDrawingsShader.md) | -|[Get-OBSDropShadowShader](Get-OBSDropShadowShader.md) | -|[Get-OBSDrunkShader](Get-OBSDrunkShader.md) | -|[Get-OBSDynamicMaskShader](Get-OBSDynamicMaskShader.md) | -|[Get-OBSEdgeDetectionShader](Get-OBSEdgeDetectionShader.md) | -|[Get-OBSEffect](Get-OBSEffect.md) | -|[Get-OBSEmbersShader](Get-OBSEmbersShader.md) | -|[Get-OBSEmbossColorShader](Get-OBSEmbossColorShader.md) | -|[Get-OBSEmbossShader](Get-OBSEmbossShader.md) | -|[Get-OBSExeldroBentCameraShader](Get-OBSExeldroBentCameraShader.md) | -|[Get-OBSFadeTransitionShader](Get-OBSFadeTransitionShader.md) | -|[Get-OBSFillColorGradientShader](Get-OBSFillColorGradientShader.md) | -|[Get-OBSFillColorLinearShader](Get-OBSFillColorLinearShader.md) | -|[Get-OBSFillColorRadialDegreesShader](Get-OBSFillColorRadialDegreesShader.md) | -|[Get-OBSFillColorRadialPercentageShader](Get-OBSFillColorRadialPercentageShader.md) | -|[Get-OBSFilterTemplateShader](Get-OBSFilterTemplateShader.md) | -|[Get-OBSFire3Shader](Get-OBSFire3Shader.md) | -|[Get-OBSFireShader](Get-OBSFireShader.md) | -|[Get-OBSFireworks2Shader](Get-OBSFireworks2Shader.md) | -|[Get-OBSFireworksShader](Get-OBSFireworksShader.md) | -|[Get-OBSFisheyeShader](Get-OBSFisheyeShader.md) | -|[Get-OBSFisheyeXyShader](Get-OBSFisheyeXyShader.md) | -|[Get-OBSFlipShader](Get-OBSFlipShader.md) | -|[Get-OBSFrostedGlassShader](Get-OBSFrostedGlassShader.md) | -|[Get-OBSGammaCorrectionShader](Get-OBSGammaCorrectionShader.md) | -|[Get-OBSGaussianBlurAdvancedShader](Get-OBSGaussianBlurAdvancedShader.md) | -|[Get-OBSGaussianBlurShader](Get-OBSGaussianBlurShader.md) | -|[Get-OBSGaussianBlurSimpleShader](Get-OBSGaussianBlurSimpleShader.md) | -|[Get-OBSGaussianExampleShader](Get-OBSGaussianExampleShader.md) | -|[Get-OBSGaussianSimpleShader](Get-OBSGaussianSimpleShader.md) | -|[Get-OBSGbCameraShader](Get-OBSGbCameraShader.md) | -|[Get-OBSGlassShader](Get-OBSGlassShader.md) | -|[Get-OBSGlitchAnalogShader](Get-OBSGlitchAnalogShader.md) | -|[Get-OBSGlitchShader](Get-OBSGlitchShader.md) | -|[Get-OBSGlowShader](Get-OBSGlowShader.md) | -|[Get-OBSGradientShader](Get-OBSGradientShader.md) | -|[Get-OBSGroup](Get-OBSGroup.md) | -|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) | -|[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | -|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | -|[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | -|[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | -|[Get-OBSHotkey](Get-OBSHotkey.md) | -|[Get-OBSHslHsvSaturationShader](Get-OBSHslHsvSaturationShader.md) | -|[Get-OBSHueRotatonShader](Get-OBSHueRotatonShader.md) | -|[Get-OBSInput](Get-OBSInput.md) | -|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) | -|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) | -|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) | -|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) | -|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) | -|[Get-OBSInputKind](Get-OBSInputKind.md) | -|[Get-OBSInputMute](Get-OBSInputMute.md) | -|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md)| -|[Get-OBSInputSettings](Get-OBSInputSettings.md) | -|[Get-OBSInputVolume](Get-OBSInputVolume.md) | -|[Get-OBSIntensityScopeShader](Get-OBSIntensityScopeShader.md) | -|[Get-OBSInvertLumaShader](Get-OBSInvertLumaShader.md) | -|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) | -|[Get-OBSLuminance2Shader](Get-OBSLuminance2Shader.md) | -|[Get-OBSLuminanceAlphaShader](Get-OBSLuminanceAlphaShader.md) | -|[Get-OBSLuminanceShader](Get-OBSLuminanceShader.md) | -|[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | -|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) | -|[Get-OBSMonitor](Get-OBSMonitor.md) | -|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | -|[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | -|[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | -|[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | -|[Get-OBSOutput](Get-OBSOutput.md) | -|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) | -|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) | -|[Get-OBSPagePeelShader](Get-OBSPagePeelShader.md) | -|[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | -|[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | -|[Get-OBSPersistentData](Get-OBSPersistentData.md) | -|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | -|[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | -|[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | -|[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | -|[Get-OBSPolarShader](Get-OBSPolarShader.md) | -|[Get-OBSProfile](Get-OBSProfile.md) | -|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) | -|[Get-OBSPulseShader](Get-OBSPulseShader.md) | -|[Get-OBSRainbowShader](Get-OBSRainbowShader.md) | -|[Get-OBSRainWindowShader](Get-OBSRainWindowShader.md) | -|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) | -|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) | -|[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | -|[Get-OBSReflectShader](Get-OBSReflectShader.md) | -|[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | -|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | -|[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | -|[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | -|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) | -|[Get-OBSRGBAPercentShader](Get-OBSRGBAPercentShader.md) | -|[Get-OBSRgbColorWheelShader](Get-OBSRgbColorWheelShader.md) | -|[Get-OBSRgbSplitShader](Get-OBSRgbSplitShader.md) | -|[Get-OBSRgbvisibilityShader](Get-OBSRgbvisibilityShader.md) | -|[Get-OBSRGSSAAShader](Get-OBSRGSSAAShader.md) | -|[Get-OBSRippleShader](Get-OBSRippleShader.md) | -|[Get-OBSRotatingSourceShader](Get-OBSRotatingSourceShader.md) | -|[Get-OBSRotatoeShader](Get-OBSRotatoeShader.md) | -|[Get-OBSRoundedRect2Shader](Get-OBSRoundedRect2Shader.md) | -|[Get-OBSRoundedRectPerCornerShader](Get-OBSRoundedRectPerCornerShader.md) | -|[Get-OBSRoundedRectPerSideShader](Get-OBSRoundedRectPerSideShader.md) | -|[Get-OBSRoundedRectShader](Get-OBSRoundedRectShader.md) | -|[Get-OBSRoundedStrokeGradientShader](Get-OBSRoundedStrokeGradientShader.md) | -|[Get-OBSRoundedStrokeShader](Get-OBSRoundedStrokeShader.md) | -|[Get-OBSScanLineShader](Get-OBSScanLineShader.md) | -|[Get-OBSScene](Get-OBSScene.md) | -|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) | -|[Get-OBSSceneItem](Get-OBSSceneItem.md) | -|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) | -|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) | -|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) | -|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) | -|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) | -|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) | -|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) | -|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) | -|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) | -|[Get-OBSSeascapeShader](Get-OBSSeascapeShader.md) | -|[Get-OBSSeasickShader](Get-OBSSeasickShader.md) | -|[Get-OBSSelectiveColorShader](Get-OBSSelectiveColorShader.md) | -|[Get-OBSShakeShader](Get-OBSShakeShader.md) | -|[Get-OBSShineShader](Get-OBSShineShader.md) | -|[Get-OBSSimpleGradientShader](Get-OBSSimpleGradientShader.md) | -|[Get-OBSSimplexNoiseShader](Get-OBSSimplexNoiseShader.md) | -|[Get-OBSSmartDenoiseShader](Get-OBSSmartDenoiseShader.md) | -|[Get-OBSSourceActive](Get-OBSSourceActive.md) | -|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) | -|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) | -|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) | -|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) | -|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) | -|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) | -|[Get-OBSSpecularShineShader](Get-OBSSpecularShineShader.md) | -|[Get-OBSSpotlightShader](Get-OBSSpotlightShader.md) | -|[Get-OBSStats](Get-OBSStats.md) | -|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) | -|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) | -|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) | -|[Get-OBSSwirlShader](Get-OBSSwirlShader.md) | -|[Get-OBSTetraShader](Get-OBSTetraShader.md) | -|[Get-OBSThermalShader](Get-OBSThermalShader.md) | -|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) | -|[Get-OBSTvCrtSubpixelShader](Get-OBSTvCrtSubpixelShader.md) | -|[Get-OBSTwistShader](Get-OBSTwistShader.md) | -|[Get-OBSTwoPassDropShadowShader](Get-OBSTwoPassDropShadowShader.md) | -|[Get-OBSVCRShader](Get-OBSVCRShader.md) | -|[Get-OBSVersion](Get-OBSVersion.md) | -|[Get-OBSVHSShader](Get-OBSVHSShader.md) | -|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) | -|[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | -|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) | -|[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | -|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | -|[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | -|[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | -|[Get-OBSZoomShader](Get-OBSZoomShader.md) | -|[Get-OBSZoomXYShader](Get-OBSZoomXYShader.md) | -|[Hide-OBS](Hide-OBS.md) | -|[Import-OBSEffect](Import-OBSEffect.md) | -|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) | -|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) | -|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) | -|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) | -|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) | -|[Receive-OBS](Receive-OBS.md) | -|[Remove-OBS](Remove-OBS.md) | -|[Remove-OBSEffect](Remove-OBSEffect.md) | -|[Remove-OBSInput](Remove-OBSInput.md) | -|[Remove-OBSProfile](Remove-OBSProfile.md) | -|[Remove-OBSScene](Remove-OBSScene.md) | -|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) | -|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) | -|[Resume-OBSRecord](Resume-OBSRecord.md) | -|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) | -|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) | -|[Send-OBS](Send-OBS.md) | -|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) | -|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) | -|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) | -|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) | -|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) | -|[Send-OBSSleep](Send-OBSSleep.md) | -|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) | -|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) | -|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) | -|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) | -|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) | -|[Set-OBS3DFilter](Set-OBS3DFilter.md) | -|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) | -|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) | -|[Set-OBSColorFilter](Set-OBSColorFilter.md) | -|[Set-OBSColorSource](Set-OBSColorSource.md) | -|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) | -|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) | -|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) | -|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) | -|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) | -|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) | -|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) | -|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) | -|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) | -|[Set-OBSGainFilter](Set-OBSGainFilter.md) | -|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) | -|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) | -|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) | -|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) | -|[Set-OBSInputMute](Set-OBSInputMute.md) | -|[Set-OBSInputName](Set-OBSInputName.md) | -|[Set-OBSInputSettings](Set-OBSInputSettings.md) | -|[Set-OBSInputVolume](Set-OBSInputVolume.md) | -|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) | -|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) | -|[Set-OBSMediaSource](Set-OBSMediaSource.md) | -|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) | -|[Set-OBSPersistentData](Set-OBSPersistentData.md) | -|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) | -|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) | -|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) | -|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) | -|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) | -|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) | -|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) | -|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) | -|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) | -|[Set-OBSSceneName](Set-OBSSceneName.md) | -|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) | -|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) | -|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) | -|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) | -|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) | -|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) | -|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) | -|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) | -|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) | -|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) | -|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) | -|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) | -|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) | -|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) | -|[Set-OBSVLCSource](Set-OBSVLCSource.md) | -|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | -|[Set-OBSWindowSource](Set-OBSWindowSource.md) | -|[Show-OBS](Show-OBS.md) | -|[Start-OBSEffect](Start-OBSEffect.md) | -|[Start-OBSOutput](Start-OBSOutput.md) | -|[Start-OBSRecord](Start-OBSRecord.md) | -|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | -|[Start-OBSStream](Start-OBSStream.md) | -|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | -|[Stop-OBSEffect](Stop-OBSEffect.md) | -|[Stop-OBSOutput](Stop-OBSOutput.md) | -|[Stop-OBSRecord](Stop-OBSRecord.md) | -|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) | -|[Stop-OBSStream](Stop-OBSStream.md) | -|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) | -|[Switch-OBSInputMute](Switch-OBSInputMute.md) | -|[Switch-OBSOutput](Switch-OBSOutput.md) | -|[Switch-OBSRecord](Switch-OBSRecord.md) | -|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) | -|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) | -|[Switch-OBSStream](Switch-OBSStream.md) | -|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) | -|[Watch-OBS](Watch-OBS.md) | +|Name |ResolvedCommand| +|------------------------------------------------------------------------------------------------|---------------| +|[Add-OBSInput](Add-OBSInput.md) | +|[Add-OBSProfile](Add-OBSProfile.md) | +|[Add-OBSScene](Add-OBSScene.md) | +|[Add-OBSSceneCollection](Add-OBSSceneCollection.md) | +|[Add-OBSSceneItem](Add-OBSSceneItem.md) | +|[Add-OBSSourceFilter](Add-OBSSourceFilter.md) | +|[Clear-OBSScene](Clear-OBSScene.md) | +|[Connect-OBS](Connect-OBS.md) | +|[Copy-OBSSceneItem](Copy-OBSSceneItem.md) | +|[Disconnect-OBS](Disconnect-OBS.md) | +|[Get-OBS](Get-OBS.md) | +|[Get-OBS3dPanelShader](Get-OBS3dPanelShader.md) | +|[Get-OBS3dSwapTransitionShader](Get-OBS3dSwapTransitionShader.md) | +|[Get-OBSAddShader](Get-OBSAddShader.md) | +|[Get-OBSAlphaBorderShader](Get-OBSAlphaBorderShader.md) | +|[Get-OBSAlphaGamingBentCameraShader](Get-OBSAlphaGamingBentCameraShader.md) | +|[Get-OBSAnimatedPathShader](Get-OBSAnimatedPathShader.md) | +|[Get-OBSAnimatedTextureShader](Get-OBSAnimatedTextureShader.md) | +|[Get-OBSAsciiShader](Get-OBSAsciiShader.md) | +|[Get-OBSAspectRatioShader](Get-OBSAspectRatioShader.md) | +|[Get-OBSAudioShader](Get-OBSAudioShader.md) | +|[Get-OBSBackgroundRemovalShader](Get-OBSBackgroundRemovalShader.md) | +|[Get-OBSBlendOpacityShader](Get-OBSBlendOpacityShader.md) | +|[Get-OBSBlinkShader](Get-OBSBlinkShader.md) | +|[Get-OBSBloomShader](Get-OBSBloomShader.md) | +|[Get-OBSBorderShader](Get-OBSBorderShader.md) | +|[Get-OBSBoxBlurShader](Get-OBSBoxBlurShader.md) | +|[Get-OBSBulgePinchShader](Get-OBSBulgePinchShader.md) | +|[Get-OBSBurnShader](Get-OBSBurnShader.md) | +|[Get-OBSCartoonShader](Get-OBSCartoonShader.md) | +|[Get-OBSCellShadedShader](Get-OBSCellShadedShader.md) | +|[Get-OBSChromaticAberrationShader](Get-OBSChromaticAberrationShader.md) | +|[Get-OBSChromaUVDistortionShader](Get-OBSChromaUVDistortionShader.md) | +|[Get-OBSCircleMaskFilterShader](Get-OBSCircleMaskFilterShader.md) | +|[Get-OBSClockAnalogShader](Get-OBSClockAnalogShader.md) | +|[Get-OBSClockDigitalLedShader](Get-OBSClockDigitalLedShader.md) | +|[Get-OBSClockDigitalNixieShader](Get-OBSClockDigitalNixieShader.md) | +|[Get-OBSColorDepthShader](Get-OBSColorDepthShader.md) | +|[Get-OBSColorGradeFilterShader](Get-OBSColorGradeFilterShader.md) | +|[Get-OBSCornerPinShader](Get-OBSCornerPinShader.md) | +|[Get-OBSCrtCurvatureShader](Get-OBSCrtCurvatureShader.md) | +|[Get-OBSCubeRotatingShader](Get-OBSCubeRotatingShader.md) | +|[Get-OBSCurrentPreviewScene](Get-OBSCurrentPreviewScene.md) | +|[Get-OBSCurrentProgramScene](Get-OBSCurrentProgramScene.md) | +|[Get-OBSCurrentSceneTransition](Get-OBSCurrentSceneTransition.md) | +|[Get-OBSCurrentSceneTransitionCursor](Get-OBSCurrentSceneTransitionCursor.md) | +|[Get-OBSCurveShader](Get-OBSCurveShader.md) | +|[Get-OBSCutRectPerCornerShader](Get-OBSCutRectPerCornerShader.md) | +|[Get-OBSCylinderShader](Get-OBSCylinderShader.md) | +|[Get-OBSDarkenShader](Get-OBSDarkenShader.md) | +|[Get-OBSDeadPixelFixerShader](Get-OBSDeadPixelFixerShader.md) | +|[Get-OBSDensitySatHueShader](Get-OBSDensitySatHueShader.md) | +|[Get-OBSDiffuseTransitionShader](Get-OBSDiffuseTransitionShader.md) | +|[Get-OBSDigitalRainShader](Get-OBSDigitalRainShader.md) | +|[Get-OBSDisplacementMapAdvancedInvertShader](Get-OBSDisplacementMapAdvancedInvertShader.md)| +|[Get-OBSDisplacementMapAdvancedShader](Get-OBSDisplacementMapAdvancedShader.md) | +|[Get-OBSDisplacementMapInvertShader](Get-OBSDisplacementMapInvertShader.md) | +|[Get-OBSDisplacementMapShader](Get-OBSDisplacementMapShader.md) | +|[Get-OBSDivideRotateShader](Get-OBSDivideRotateShader.md) | +|[Get-OBSDoodleShader](Get-OBSDoodleShader.md) | +|[Get-OBSDrawingsShader](Get-OBSDrawingsShader.md) | +|[Get-OBSDropShadowShader](Get-OBSDropShadowShader.md) | +|[Get-OBSDrunkShader](Get-OBSDrunkShader.md) | +|[Get-OBSDynamicMaskShader](Get-OBSDynamicMaskShader.md) | +|[Get-OBSEdgeDetectionShader](Get-OBSEdgeDetectionShader.md) | +|[Get-OBSEffect](Get-OBSEffect.md) | +|[Get-OBSEmbersShader](Get-OBSEmbersShader.md) | +|[Get-OBSEmbossColorShader](Get-OBSEmbossColorShader.md) | +|[Get-OBSEmbossShader](Get-OBSEmbossShader.md) | +|[Get-OBSExeldroBentCameraShader](Get-OBSExeldroBentCameraShader.md) | +|[Get-OBSFadeTransitionShader](Get-OBSFadeTransitionShader.md) | +|[Get-OBSFillColorGradientShader](Get-OBSFillColorGradientShader.md) | +|[Get-OBSFillColorLinearShader](Get-OBSFillColorLinearShader.md) | +|[Get-OBSFillColorRadialDegreesShader](Get-OBSFillColorRadialDegreesShader.md) | +|[Get-OBSFillColorRadialPercentageShader](Get-OBSFillColorRadialPercentageShader.md) | +|[Get-OBSFilterTemplateShader](Get-OBSFilterTemplateShader.md) | +|[Get-OBSFire3Shader](Get-OBSFire3Shader.md) | +|[Get-OBSFireShader](Get-OBSFireShader.md) | +|[Get-OBSFireworks2Shader](Get-OBSFireworks2Shader.md) | +|[Get-OBSFireworksShader](Get-OBSFireworksShader.md) | +|[Get-OBSFisheyeShader](Get-OBSFisheyeShader.md) | +|[Get-OBSFisheyeXyShader](Get-OBSFisheyeXyShader.md) | +|[Get-OBSFlipShader](Get-OBSFlipShader.md) | +|[Get-OBSFrostedGlassShader](Get-OBSFrostedGlassShader.md) | +|[Get-OBSGammaCorrectionShader](Get-OBSGammaCorrectionShader.md) | +|[Get-OBSGaussianBlurAdvancedShader](Get-OBSGaussianBlurAdvancedShader.md) | +|[Get-OBSGaussianBlurShader](Get-OBSGaussianBlurShader.md) | +|[Get-OBSGaussianBlurSimpleShader](Get-OBSGaussianBlurSimpleShader.md) | +|[Get-OBSGaussianExampleShader](Get-OBSGaussianExampleShader.md) | +|[Get-OBSGaussianSimpleShader](Get-OBSGaussianSimpleShader.md) | +|[Get-OBSGbCameraShader](Get-OBSGbCameraShader.md) | +|[Get-OBSGlassShader](Get-OBSGlassShader.md) | +|[Get-OBSGlitchAnalogShader](Get-OBSGlitchAnalogShader.md) | +|[Get-OBSGlitchPeriodicShader](Get-OBSGlitchPeriodicShader.md) | +|[Get-OBSGlitchShader](Get-OBSGlitchShader.md) | +|[Get-OBSGlowShader](Get-OBSGlowShader.md) | +|[Get-OBSGradientShader](Get-OBSGradientShader.md) | +|[Get-OBSGroup](Get-OBSGroup.md) | +|[Get-OBSGroupSceneItem](Get-OBSGroupSceneItem.md) | +|[Get-OBSHalftoneShader](Get-OBSHalftoneShader.md) | +|[Get-OBSHardBlinkShader](Get-OBSHardBlinkShader.md) | +|[Get-OBSHeatWaveSimpleShader](Get-OBSHeatWaveSimpleShader.md) | +|[Get-OBSHexagonShader](Get-OBSHexagonShader.md) | +|[Get-OBSHotkey](Get-OBSHotkey.md) | +|[Get-OBSHslHsvSaturationShader](Get-OBSHslHsvSaturationShader.md) | +|[Get-OBSHueRotatonShader](Get-OBSHueRotatonShader.md) | +|[Get-OBSInput](Get-OBSInput.md) | +|[Get-OBSInputAudioBalance](Get-OBSInputAudioBalance.md) | +|[Get-OBSInputAudioMonitorType](Get-OBSInputAudioMonitorType.md) | +|[Get-OBSInputAudioSyncOffset](Get-OBSInputAudioSyncOffset.md) | +|[Get-OBSInputAudioTracks](Get-OBSInputAudioTracks.md) | +|[Get-OBSInputDefaultSettings](Get-OBSInputDefaultSettings.md) | +|[Get-OBSInputKind](Get-OBSInputKind.md) | +|[Get-OBSInputMute](Get-OBSInputMute.md) | +|[Get-OBSInputPropertiesListPropertyItems](Get-OBSInputPropertiesListPropertyItems.md) | +|[Get-OBSInputSettings](Get-OBSInputSettings.md) | +|[Get-OBSInputVolume](Get-OBSInputVolume.md) | +|[Get-OBSIntensityScopeShader](Get-OBSIntensityScopeShader.md) | +|[Get-OBSInvertLumaShader](Get-OBSInvertLumaShader.md) | +|[Get-OBSLastReplayBufferReplay](Get-OBSLastReplayBufferReplay.md) | +|[Get-OBSLuminance2Shader](Get-OBSLuminance2Shader.md) | +|[Get-OBSLuminanceAlphaShader](Get-OBSLuminanceAlphaShader.md) | +|[Get-OBSLuminanceShader](Get-OBSLuminanceShader.md) | +|[Get-OBSMatrixShader](Get-OBSMatrixShader.md) | +|[Get-OBSMediaInputStatus](Get-OBSMediaInputStatus.md) | +|[Get-OBSMonitor](Get-OBSMonitor.md) | +|[Get-OBSMotionBlurShader](Get-OBSMotionBlurShader.md) | +|[Get-OBSMultiplyShader](Get-OBSMultiplyShader.md) | +|[Get-OBSNightSkyShader](Get-OBSNightSkyShader.md) | +|[Get-OBSNoiseShader](Get-OBSNoiseShader.md) | +|[Get-OBSNormalMapShader](Get-OBSNormalMapShader.md) | +|[Get-OBSOpacityShader](Get-OBSOpacityShader.md) | +|[Get-OBSOutput](Get-OBSOutput.md) | +|[Get-OBSOutputSettings](Get-OBSOutputSettings.md) | +|[Get-OBSOutputStatus](Get-OBSOutputStatus.md) | +|[Get-OBSPagePeelShader](Get-OBSPagePeelShader.md) | +|[Get-OBSPagePeelTransitionShader](Get-OBSPagePeelTransitionShader.md) | +|[Get-OBSPerlinNoiseShader](Get-OBSPerlinNoiseShader.md) | +|[Get-OBSPersistentData](Get-OBSPersistentData.md) | +|[Get-OBSPerspectiveShader](Get-OBSPerspectiveShader.md) | +|[Get-OBSPieChartShader](Get-OBSPieChartShader.md) | +|[Get-OBSPixelationShader](Get-OBSPixelationShader.md) | +|[Get-OBSPixelationTransitionShader](Get-OBSPixelationTransitionShader.md) | +|[Get-OBSPolarShader](Get-OBSPolarShader.md) | +|[Get-OBSProfile](Get-OBSProfile.md) | +|[Get-OBSProfileParameter](Get-OBSProfileParameter.md) | +|[Get-OBSPulseShader](Get-OBSPulseShader.md) | +|[Get-OBSQuadrilateralCropShader](Get-OBSQuadrilateralCropShader.md) | +|[Get-OBSRainbowShader](Get-OBSRainbowShader.md) | +|[Get-OBSRainWindowShader](Get-OBSRainWindowShader.md) | +|[Get-OBSRecordDirectory](Get-OBSRecordDirectory.md) | +|[Get-OBSRecordStatus](Get-OBSRecordStatus.md) | +|[Get-OBSRectangularDropShadowShader](Get-OBSRectangularDropShadowShader.md) | +|[Get-OBSReflectShader](Get-OBSReflectShader.md) | +|[Get-OBSRemovePartialPixelsShader](Get-OBSRemovePartialPixelsShader.md) | +|[Get-OBSRepeatGridCenterCropShader](Get-OBSRepeatGridCenterCropShader.md) | +|[Get-OBSRepeatShader](Get-OBSRepeatShader.md) | +|[Get-OBSRepeatTextureShader](Get-OBSRepeatTextureShader.md) | +|[Get-OBSReplayBufferStatus](Get-OBSReplayBufferStatus.md) | +|[Get-OBSRGBAPercentShader](Get-OBSRGBAPercentShader.md) | +|[Get-OBSRgbColorWheelShader](Get-OBSRgbColorWheelShader.md) | +|[Get-OBSRgbSplitShader](Get-OBSRgbSplitShader.md) | +|[Get-OBSRgbvisibilityShader](Get-OBSRgbvisibilityShader.md) | +|[Get-OBSRGSSAAShader](Get-OBSRGSSAAShader.md) | +|[Get-OBSRippleShader](Get-OBSRippleShader.md) | +|[Get-OBSRotatingSourceShader](Get-OBSRotatingSourceShader.md) | +|[Get-OBSRotatoeShader](Get-OBSRotatoeShader.md) | +|[Get-OBSRoundedRect2Shader](Get-OBSRoundedRect2Shader.md) | +|[Get-OBSRoundedRectPerCornerShader](Get-OBSRoundedRectPerCornerShader.md) | +|[Get-OBSRoundedRectPerSideShader](Get-OBSRoundedRectPerSideShader.md) | +|[Get-OBSRoundedRectShader](Get-OBSRoundedRectShader.md) | +|[Get-OBSRoundedStrokeGradientShader](Get-OBSRoundedStrokeGradientShader.md) | +|[Get-OBSRoundedStrokeShader](Get-OBSRoundedStrokeShader.md) | +|[Get-OBSScanLineShader](Get-OBSScanLineShader.md) | +|[Get-OBSScene](Get-OBSScene.md) | +|[Get-OBSSceneCollection](Get-OBSSceneCollection.md) | +|[Get-OBSSceneItem](Get-OBSSceneItem.md) | +|[Get-OBSSceneItemBlendMode](Get-OBSSceneItemBlendMode.md) | +|[Get-OBSSceneItemEnabled](Get-OBSSceneItemEnabled.md) | +|[Get-OBSSceneItemId](Get-OBSSceneItemId.md) | +|[Get-OBSSceneItemIndex](Get-OBSSceneItemIndex.md) | +|[Get-OBSSceneItemLocked](Get-OBSSceneItemLocked.md) | +|[Get-OBSSceneItemSource](Get-OBSSceneItemSource.md) | +|[Get-OBSSceneItemTransform](Get-OBSSceneItemTransform.md) | +|[Get-OBSSceneSceneTransitionOverride](Get-OBSSceneSceneTransitionOverride.md) | +|[Get-OBSSceneTransition](Get-OBSSceneTransition.md) | +|[Get-OBSSeascapeShader](Get-OBSSeascapeShader.md) | +|[Get-OBSSeasickShader](Get-OBSSeasickShader.md) | +|[Get-OBSSelectiveColorShader](Get-OBSSelectiveColorShader.md) | +|[Get-OBSShakeShader](Get-OBSShakeShader.md) | +|[Get-OBSShineShader](Get-OBSShineShader.md) | +|[Get-OBSSimpleGradientShader](Get-OBSSimpleGradientShader.md) | +|[Get-OBSSimplexNoiseShader](Get-OBSSimplexNoiseShader.md) | +|[Get-OBSSmartDenoiseShader](Get-OBSSmartDenoiseShader.md) | +|[Get-OBSSourceActive](Get-OBSSourceActive.md) | +|[Get-OBSSourceFilter](Get-OBSSourceFilter.md) | +|[Get-OBSSourceFilterDefaultSettings](Get-OBSSourceFilterDefaultSettings.md) | +|[Get-OBSSourceFilterKind](Get-OBSSourceFilterKind.md) | +|[Get-OBSSourceFilterList](Get-OBSSourceFilterList.md) | +|[Get-OBSSourceScreenshot](Get-OBSSourceScreenshot.md) | +|[Get-OBSSpecialInputs](Get-OBSSpecialInputs.md) | +|[Get-OBSSpecularShineShader](Get-OBSSpecularShineShader.md) | +|[Get-OBSSpotlightShader](Get-OBSSpotlightShader.md) | +|[Get-OBSStats](Get-OBSStats.md) | +|[Get-OBSStreamServiceSettings](Get-OBSStreamServiceSettings.md) | +|[Get-OBSStreamStatus](Get-OBSStreamStatus.md) | +|[Get-OBSStudioModeEnabled](Get-OBSStudioModeEnabled.md) | +|[Get-OBSSwirlShader](Get-OBSSwirlShader.md) | +|[Get-OBSTetraShader](Get-OBSTetraShader.md) | +|[Get-OBSThermalShader](Get-OBSThermalShader.md) | +|[Get-OBSTransitionKind](Get-OBSTransitionKind.md) | +|[Get-OBSTvCrtSubpixelShader](Get-OBSTvCrtSubpixelShader.md) | +|[Get-OBSTwistShader](Get-OBSTwistShader.md) | +|[Get-OBSTwoPassDropShadowShader](Get-OBSTwoPassDropShadowShader.md) | +|[Get-OBSVCRShader](Get-OBSVCRShader.md) | +|[Get-OBSVersion](Get-OBSVersion.md) | +|[Get-OBSVHSShader](Get-OBSVHSShader.md) | +|[Get-OBSVideoSettings](Get-OBSVideoSettings.md) | +|[Get-OBSVignettingShader](Get-OBSVignettingShader.md) | +|[Get-OBSVirtualCamStatus](Get-OBSVirtualCamStatus.md) | +|[Get-OBSVoronoiPixelationShader](Get-OBSVoronoiPixelationShader.md) | +|[Get-OBSWalkingDeadPixelFixerShader](Get-OBSWalkingDeadPixelFixerShader.md) | +|[Get-OBSZigZagShader](Get-OBSZigZagShader.md) | +|[Get-OBSZoomBlurShader](Get-OBSZoomBlurShader.md) | +|[Get-OBSZoomBlurTransitionShader](Get-OBSZoomBlurTransitionShader.md) | +|[Get-OBSZoomShader](Get-OBSZoomShader.md) | +|[Get-OBSZoomXYShader](Get-OBSZoomXYShader.md) | +|[Hide-OBS](Hide-OBS.md) | +|[Import-OBSEffect](Import-OBSEffect.md) | +|[Open-OBSInputFiltersDialog](Open-OBSInputFiltersDialog.md) | +|[Open-OBSInputInteractDialog](Open-OBSInputInteractDialog.md) | +|[Open-OBSInputPropertiesDialog](Open-OBSInputPropertiesDialog.md) | +|[Open-OBSSourceProjector](Open-OBSSourceProjector.md) | +|[Open-OBSVideoMixProjector](Open-OBSVideoMixProjector.md) | +|[Receive-OBS](Receive-OBS.md) | +|[Remove-OBS](Remove-OBS.md) | +|[Remove-OBSEffect](Remove-OBSEffect.md) | +|[Remove-OBSInput](Remove-OBSInput.md) | +|[Remove-OBSProfile](Remove-OBSProfile.md) | +|[Remove-OBSScene](Remove-OBSScene.md) | +|[Remove-OBSSceneItem](Remove-OBSSceneItem.md) | +|[Remove-OBSSourceFilter](Remove-OBSSourceFilter.md) | +|[Resume-OBSRecord](Resume-OBSRecord.md) | +|[Save-OBSReplayBuffer](Save-OBSReplayBuffer.md) | +|[Save-OBSSourceScreenshot](Save-OBSSourceScreenshot.md) | +|[Send-OBS](Send-OBS.md) | +|[Send-OBSCallVendorRequest](Send-OBSCallVendorRequest.md) | +|[Send-OBSCustomEvent](Send-OBSCustomEvent.md) | +|[Send-OBSOffsetMediaInputCursor](Send-OBSOffsetMediaInputCursor.md) | +|[Send-OBSPauseRecord](Send-OBSPauseRecord.md) | +|[Send-OBSPressInputPropertiesButton](Send-OBSPressInputPropertiesButton.md) | +|[Send-OBSSleep](Send-OBSSleep.md) | +|[Send-OBSStreamCaption](Send-OBSStreamCaption.md) | +|[Send-OBSTriggerHotkeyByKeySequence](Send-OBSTriggerHotkeyByKeySequence.md) | +|[Send-OBSTriggerHotkeyByName](Send-OBSTriggerHotkeyByName.md) | +|[Send-OBSTriggerMediaInputAction](Send-OBSTriggerMediaInputAction.md) | +|[Send-OBSTriggerStudioModeTransition](Send-OBSTriggerStudioModeTransition.md) | +|[Set-OBS3DFilter](Set-OBS3DFilter.md) | +|[Set-OBSAudioOutputSource](Set-OBSAudioOutputSource.md) | +|[Set-OBSBrowserSource](Set-OBSBrowserSource.md) | +|[Set-OBSColorFilter](Set-OBSColorFilter.md) | +|[Set-OBSColorSource](Set-OBSColorSource.md) | +|[Set-OBSCurrentPreviewScene](Set-OBSCurrentPreviewScene.md) | +|[Set-OBSCurrentProfile](Set-OBSCurrentProfile.md) | +|[Set-OBSCurrentProgramScene](Set-OBSCurrentProgramScene.md) | +|[Set-OBSCurrentSceneCollection](Set-OBSCurrentSceneCollection.md) | +|[Set-OBSCurrentSceneTransition](Set-OBSCurrentSceneTransition.md) | +|[Set-OBSCurrentSceneTransitionDuration](Set-OBSCurrentSceneTransitionDuration.md) | +|[Set-OBSCurrentSceneTransitionSettings](Set-OBSCurrentSceneTransitionSettings.md) | +|[Set-OBSDisplaySource](Set-OBSDisplaySource.md) | +|[Set-OBSEqualizerFilter](Set-OBSEqualizerFilter.md) | +|[Set-OBSGainFilter](Set-OBSGainFilter.md) | +|[Set-OBSInputAudioBalance](Set-OBSInputAudioBalance.md) | +|[Set-OBSInputAudioMonitorType](Set-OBSInputAudioMonitorType.md) | +|[Set-OBSInputAudioSyncOffset](Set-OBSInputAudioSyncOffset.md) | +|[Set-OBSInputAudioTracks](Set-OBSInputAudioTracks.md) | +|[Set-OBSInputMute](Set-OBSInputMute.md) | +|[Set-OBSInputName](Set-OBSInputName.md) | +|[Set-OBSInputSettings](Set-OBSInputSettings.md) | +|[Set-OBSInputVolume](Set-OBSInputVolume.md) | +|[Set-OBSMarkdownSource](Set-OBSMarkdownSource.md) | +|[Set-OBSMediaInputCursor](Set-OBSMediaInputCursor.md) | +|[Set-OBSMediaSource](Set-OBSMediaSource.md) | +|[Set-OBSOutputSettings](Set-OBSOutputSettings.md) | +|[Set-OBSPersistentData](Set-OBSPersistentData.md) | +|[Set-OBSProfileParameter](Set-OBSProfileParameter.md) | +|[Set-OBSRecordDirectory](Set-OBSRecordDirectory.md) | +|[Set-OBSRenderDelayFilter](Set-OBSRenderDelayFilter.md) | +|[Set-OBSScaleFilter](Set-OBSScaleFilter.md) | +|[Set-OBSSceneItemBlendMode](Set-OBSSceneItemBlendMode.md) | +|[Set-OBSSceneItemEnabled](Set-OBSSceneItemEnabled.md) | +|[Set-OBSSceneItemIndex](Set-OBSSceneItemIndex.md) | +|[Set-OBSSceneItemLocked](Set-OBSSceneItemLocked.md) | +|[Set-OBSSceneItemTransform](Set-OBSSceneItemTransform.md) | +|[Set-OBSSceneName](Set-OBSSceneName.md) | +|[Set-OBSSceneSceneTransitionOverride](Set-OBSSceneSceneTransitionOverride.md) | +|[Set-OBSScrollFilter](Set-OBSScrollFilter.md) | +|[Set-OBSShaderFilter](Set-OBSShaderFilter.md) | +|[Set-OBSSharpnessFilter](Set-OBSSharpnessFilter.md) | +|[Set-OBSSoundCloudSource](Set-OBSSoundCloudSource.md) | +|[Set-OBSSourceFilterEnabled](Set-OBSSourceFilterEnabled.md) | +|[Set-OBSSourceFilterIndex](Set-OBSSourceFilterIndex.md) | +|[Set-OBSSourceFilterName](Set-OBSSourceFilterName.md) | +|[Set-OBSSourceFilterSettings](Set-OBSSourceFilterSettings.md) | +|[Set-OBSStreamServiceSettings](Set-OBSStreamServiceSettings.md) | +|[Set-OBSStudioModeEnabled](Set-OBSStudioModeEnabled.md) | +|[Set-OBSSwitchSource](Set-OBSSwitchSource.md) | +|[Set-OBSTBarPosition](Set-OBSTBarPosition.md) | +|[Set-OBSVideoSettings](Set-OBSVideoSettings.md) | +|[Set-OBSVLCSource](Set-OBSVLCSource.md) | +|[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | +|[Set-OBSWindowSource](Set-OBSWindowSource.md) | +|[Show-OBS](Show-OBS.md) | +|[Start-OBSEffect](Start-OBSEffect.md) | +|[Start-OBSOutput](Start-OBSOutput.md) | +|[Start-OBSRecord](Start-OBSRecord.md) | +|[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | +|[Start-OBSStream](Start-OBSStream.md) | +|[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | +|[Stop-OBSEffect](Stop-OBSEffect.md) | +|[Stop-OBSOutput](Stop-OBSOutput.md) | +|[Stop-OBSRecord](Stop-OBSRecord.md) | +|[Stop-OBSReplayBuffer](Stop-OBSReplayBuffer.md) | +|[Stop-OBSStream](Stop-OBSStream.md) | +|[Stop-OBSVirtualCam](Stop-OBSVirtualCam.md) | +|[Switch-OBSInputMute](Switch-OBSInputMute.md) | +|[Switch-OBSOutput](Switch-OBSOutput.md) | +|[Switch-OBSRecord](Switch-OBSRecord.md) | +|[Switch-OBSRecordPause](Switch-OBSRecordPause.md) | +|[Switch-OBSReplayBuffer](Switch-OBSReplayBuffer.md) | +|[Switch-OBSStream](Switch-OBSStream.md) | +|[Switch-OBSVirtualCam](Switch-OBSVirtualCam.md) | +|[Watch-OBS](Watch-OBS.md) | From 42441c3b6df82ee33e0bf28053baaa5d4bbf153f Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:22:08 +0000 Subject: [PATCH 249/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- obs-powershell-Help.xml | 887 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 857 insertions(+), 30 deletions(-) diff --git a/obs-powershell-Help.xml b/obs-powershell-Help.xml index e7441f79..3c72ec4f 100644 --- a/obs-powershell-Help.xml +++ b/obs-powershell-Help.xml @@ -5873,9 +5873,7 @@ This can increase performance, and also silently ignore critical errorsOBSAudioShader Get - -Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float>] [[-Intensity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float>] [[-Intensity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -5887,6 +5885,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> AudioPeak + + Float @@ -5899,6 +5899,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> AudioMagnitude + + Float @@ -5911,6 +5913,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> Intensity + + Float @@ -5923,6 +5927,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> SourceName + + String @@ -5935,6 +5941,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> FilterName + + String @@ -5947,6 +5955,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> ShaderText + + String @@ -5959,6 +5969,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> Force + + Switch @@ -5971,6 +5983,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> PassThru + + Switch @@ -5983,6 +5997,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> NoResponse + + Switch @@ -5995,6 +6011,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> UseShaderTime + + Switch @@ -6010,6 +6028,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> AudioMagnitude + + Float @@ -6022,6 +6042,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> AudioPeak + + Float @@ -6034,6 +6056,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> FilterName + + String @@ -6046,6 +6070,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> Force + + Switch @@ -6058,6 +6084,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> Intensity + + Float @@ -6070,6 +6098,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> NoResponse + + Switch @@ -6082,6 +6112,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> PassThru + + Switch @@ -6094,6 +6126,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> ShaderText + + String @@ -6106,6 +6140,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> SourceName + + String @@ -6118,6 +6154,8 @@ Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float> UseShaderTime + + Switch @@ -18751,9 +18789,7 @@ This can increase performance, and also silently ignore critical errorsOBSDisplacementMapAdvancedInvertShader Get - -Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -18765,6 +18801,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementInfo + + String @@ -18777,6 +18815,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementX + + Float @@ -18789,6 +18829,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementY + + Float @@ -18801,6 +18843,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementCurve + + Int @@ -18813,6 +18857,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurInfo + + String @@ -18825,6 +18871,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurSize + + Float @@ -18837,6 +18885,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurQuality + + Float @@ -18849,6 +18899,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurDirections + + Float @@ -18861,6 +18913,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurAngle + + Float @@ -18873,6 +18927,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ChromaticAberrationInfo + + String @@ -18885,6 +18941,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ChromaticAberration + + Float @@ -18897,6 +18955,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ColorizeInfo + + String @@ -18909,6 +18969,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ColorizeColor + + String @@ -18921,6 +18983,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] FlagsInfo + + String @@ -18933,6 +18997,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsStrength + + Switch @@ -18945,6 +19011,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsColorize + + Switch @@ -18957,6 +19025,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsBlur + + Switch @@ -18969,6 +19039,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] AlphaAffectsStrength + + Switch @@ -18981,6 +19053,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ApplyAlpha + + Switch @@ -18993,6 +19067,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BackgroundLayer + + String @@ -19005,6 +19081,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] SourceName + + String @@ -19017,6 +19095,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] FilterName + + String @@ -19029,6 +19109,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ShaderText + + String @@ -19041,6 +19123,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] Force + + Switch @@ -19053,6 +19137,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] PassThru + + Switch @@ -19065,6 +19151,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] NoResponse + + Switch @@ -19077,6 +19165,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] UseShaderTime + + Switch @@ -19092,6 +19182,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] AlphaAffectsStrength + + Switch @@ -19104,6 +19196,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ApplyAlpha + + Switch @@ -19116,6 +19210,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BackgroundLayer + + String @@ -19128,6 +19224,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsBlur + + Switch @@ -19140,6 +19238,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsColorize + + Switch @@ -19152,6 +19252,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlueAffectsStrength + + Switch @@ -19164,6 +19266,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurAngle + + Float @@ -19176,6 +19280,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurDirections + + Float @@ -19188,6 +19294,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurInfo + + String @@ -19200,6 +19308,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurQuality + + Float @@ -19212,6 +19322,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] BlurSize + + Float @@ -19224,6 +19336,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ChromaticAberration + + Float @@ -19236,6 +19350,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ChromaticAberrationInfo + + String @@ -19248,6 +19364,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ColorizeColor + + String @@ -19260,6 +19378,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ColorizeInfo + + String @@ -19272,6 +19392,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementCurve + + Int @@ -19284,6 +19406,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementInfo + + String @@ -19296,6 +19420,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementX + + Float @@ -19308,6 +19434,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] DisplacementY + + Float @@ -19320,6 +19448,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] FilterName + + String @@ -19332,6 +19462,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] FlagsInfo + + String @@ -19344,6 +19476,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] Force + + Switch @@ -19356,6 +19490,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] NoResponse + + Switch @@ -19368,6 +19504,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] PassThru + + Switch @@ -19380,6 +19518,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] ShaderText + + String @@ -19392,6 +19532,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] SourceName + + String @@ -19404,6 +19546,8 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] UseShaderTime + + Switch @@ -19439,9 +19583,7 @@ Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] OBSDisplacementMapAdvancedShader Get - -Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -19453,6 +19595,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementInfo + + String @@ -19465,6 +19609,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementX + + Float @@ -19477,6 +19623,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementY + + Float @@ -19489,6 +19637,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementCurve + + Int @@ -19501,6 +19651,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurInfo + + String @@ -19513,6 +19665,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurSize + + Float @@ -19525,6 +19679,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurQuality + + Float @@ -19537,6 +19693,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurDirections + + Float @@ -19549,6 +19707,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurAngle + + Float @@ -19561,6 +19721,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ChromaticAberrationInfo + + String @@ -19573,6 +19735,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ChromaticAberration + + Float @@ -19585,6 +19749,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ColorizeInfo + + String @@ -19597,6 +19763,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ColorizeColor + + String @@ -19609,6 +19777,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis FlagsInfo + + String @@ -19621,6 +19791,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsStrength + + Switch @@ -19633,6 +19805,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsColorize + + Switch @@ -19645,6 +19819,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsBlur + + Switch @@ -19657,6 +19833,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis AlphaAffectsStrength + + Switch @@ -19669,6 +19847,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ApplyAlpha + + Switch @@ -19681,6 +19861,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis MaskLayer + + String @@ -19693,6 +19875,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis SourceName + + String @@ -19705,6 +19889,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis FilterName + + String @@ -19717,6 +19903,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ShaderText + + String @@ -19729,6 +19917,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis Force + + Switch @@ -19741,6 +19931,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis PassThru + + Switch @@ -19753,6 +19945,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis NoResponse + + Switch @@ -19765,6 +19959,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis UseShaderTime + + Switch @@ -19780,6 +19976,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis AlphaAffectsStrength + + Switch @@ -19792,6 +19990,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ApplyAlpha + + Switch @@ -19804,6 +20004,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsBlur + + Switch @@ -19816,6 +20018,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsColorize + + Switch @@ -19828,6 +20032,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlueAffectsStrength + + Switch @@ -19840,6 +20046,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurAngle + + Float @@ -19852,6 +20060,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurDirections + + Float @@ -19864,6 +20074,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurInfo + + String @@ -19876,6 +20088,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurQuality + + Float @@ -19888,6 +20102,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis BlurSize + + Float @@ -19900,6 +20116,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ChromaticAberration + + Float @@ -19912,6 +20130,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ChromaticAberrationInfo + + String @@ -19924,6 +20144,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ColorizeColor + + String @@ -19936,6 +20158,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ColorizeInfo + + String @@ -19948,6 +20172,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementCurve + + Int @@ -19960,6 +20186,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementInfo + + String @@ -19972,6 +20200,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementX + + Float @@ -19984,6 +20214,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis DisplacementY + + Float @@ -19996,6 +20228,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis FilterName + + String @@ -20008,6 +20242,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis FlagsInfo + + String @@ -20020,6 +20256,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis Force + + Switch @@ -20032,6 +20270,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis MaskLayer + + String @@ -20044,6 +20284,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis NoResponse + + Switch @@ -20056,6 +20298,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis PassThru + + Switch @@ -20068,6 +20312,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis ShaderText + + String @@ -20080,6 +20326,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis SourceName + + String @@ -20092,6 +20340,8 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis UseShaderTime + + Switch @@ -20127,9 +20377,7 @@ Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-Dis OBSDisplacementMapInvertShader Get - -Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -20141,6 +20389,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementInfo + + String @@ -20153,6 +20403,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementX + + Float @@ -20165,6 +20417,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementY + + Float @@ -20177,6 +20431,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ BackgroundLayer + + String @@ -20189,6 +20445,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ SourceName + + String @@ -20201,6 +20459,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ FilterName + + String @@ -20213,6 +20473,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ ShaderText + + String @@ -20225,6 +20487,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ Force + + Switch @@ -20237,6 +20501,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ PassThru + + Switch @@ -20249,6 +20515,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ NoResponse + + Switch @@ -20261,6 +20529,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ UseShaderTime + + Switch @@ -20276,6 +20546,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ BackgroundLayer + + String @@ -20288,6 +20560,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementInfo + + String @@ -20300,6 +20574,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementX + + Float @@ -20312,6 +20588,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ DisplacementY + + Float @@ -20324,6 +20602,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ FilterName + + String @@ -20336,6 +20616,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ Force + + Switch @@ -20348,6 +20630,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ NoResponse + + Switch @@ -20360,6 +20644,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ PassThru + + Switch @@ -20372,6 +20658,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ ShaderText + + String @@ -20384,6 +20672,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ SourceName + + String @@ -20396,6 +20686,8 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ UseShaderTime + + Switch @@ -20431,9 +20723,7 @@ Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-Displ OBSDisplacementMapShader Get - -Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -20445,6 +20735,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementInfo + + String @@ -20457,6 +20749,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementX + + Float @@ -20469,6 +20763,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementY + + Float @@ -20481,6 +20777,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen MaskLayer + + String @@ -20493,6 +20791,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen SourceName + + String @@ -20505,6 +20805,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen FilterName + + String @@ -20517,6 +20819,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen ShaderText + + String @@ -20529,6 +20833,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen Force + + Switch @@ -20541,6 +20847,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen PassThru + + Switch @@ -20553,6 +20861,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen NoResponse + + Switch @@ -20565,6 +20875,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen UseShaderTime + + Switch @@ -20580,6 +20892,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementInfo + + String @@ -20592,6 +20906,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementX + + Float @@ -20604,6 +20920,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen DisplacementY + + Float @@ -20616,6 +20934,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen FilterName + + String @@ -20628,6 +20948,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen Force + + Switch @@ -20640,6 +20962,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen MaskLayer + + String @@ -20652,6 +20976,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen NoResponse + + Switch @@ -20664,6 +20990,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen PassThru + + Switch @@ -20676,6 +21004,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen ShaderText + + String @@ -20688,6 +21018,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen SourceName + + String @@ -20700,6 +21032,8 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen UseShaderTime + + Switch @@ -37184,9 +37518,7 @@ Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-Displacemen OBSGlitchPeriodicShader Get - -Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-AMPL] <float>] [[-SCRA] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-AMPL] <float>] [[-SCRA] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -37198,6 +37530,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A PERI + + Float @@ -37210,6 +37544,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A DURA + + Float @@ -37222,6 +37558,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A AMPL + + Float @@ -37234,6 +37572,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A SCRA + + Float @@ -37246,6 +37586,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A SourceName + + String @@ -37258,6 +37600,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A FilterName + + String @@ -37270,6 +37614,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A ShaderText + + String @@ -37282,6 +37628,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A Force + + Switch @@ -37294,6 +37642,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A PassThru + + Switch @@ -37306,6 +37656,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A NoResponse + + Switch @@ -37318,6 +37670,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A UseShaderTime + + Switch @@ -37333,6 +37687,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A AMPL + + Float @@ -37345,6 +37701,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A DURA + + Float @@ -37357,6 +37715,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A FilterName + + String @@ -37369,6 +37729,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A Force + + Switch @@ -37381,6 +37743,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A NoResponse + + Switch @@ -37393,6 +37757,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A PassThru + + Switch @@ -37405,6 +37771,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A PERI + + Float @@ -37417,6 +37785,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A SCRA + + Float @@ -37429,6 +37799,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A ShaderText + + String @@ -37441,6 +37813,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A SourceName + + String @@ -37453,6 +37827,8 @@ Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-A UseShaderTime + + Switch @@ -47383,9 +47759,7 @@ This can increase performance, and also silently ignore critical errorsOBSNoiseShader Get - -Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLevel] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLevel] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -47397,6 +47771,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Speed + + Float @@ -47409,6 +47785,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Scale + + Float @@ -47421,6 +47799,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev NoiseLevel + + Float @@ -47433,6 +47813,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Monochromatic + + Switch @@ -47445,6 +47827,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev UseRand + + Switch @@ -47457,6 +47841,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev SourceName + + String @@ -47469,6 +47855,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev FilterName + + String @@ -47481,6 +47869,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev ShaderText + + String @@ -47493,6 +47883,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Force + + Switch @@ -47505,6 +47897,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev PassThru + + Switch @@ -47517,6 +47911,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev NoResponse + + Switch @@ -47529,6 +47925,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev UseShaderTime + + Switch @@ -47544,6 +47942,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev FilterName + + String @@ -47556,6 +47956,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Force + + Switch @@ -47568,6 +47970,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Monochromatic + + Switch @@ -47580,6 +47984,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev NoiseLevel + + Float @@ -47592,6 +47998,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev NoResponse + + Switch @@ -47604,6 +48012,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev PassThru + + Switch @@ -47616,6 +48026,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Scale + + Float @@ -47628,6 +48040,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev ShaderText + + String @@ -47640,6 +48054,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev SourceName + + String @@ -47652,6 +48068,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev Speed + + Float @@ -47664,6 +48082,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev UseRand + + Switch @@ -47676,6 +48096,8 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev UseShaderTime + + Switch @@ -47711,9 +48133,7 @@ Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLev OBSNormalMapShader Get - -Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -47725,6 +48145,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Strength + + Float @@ -47737,6 +48159,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour OffsetHeight + + Switch @@ -47749,6 +48173,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertR + + Switch @@ -47761,6 +48187,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertG + + Switch @@ -47773,6 +48201,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertH + + Switch @@ -47785,6 +48215,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Type + + Int @@ -47797,6 +48229,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour SourceName + + String @@ -47809,6 +48243,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour FilterName + + String @@ -47821,6 +48257,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour ShaderText + + String @@ -47833,6 +48271,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Force + + Switch @@ -47845,6 +48285,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour PassThru + + Switch @@ -47857,6 +48299,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour NoResponse + + Switch @@ -47869,6 +48313,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour UseShaderTime + + Switch @@ -47884,6 +48330,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour FilterName + + String @@ -47896,6 +48344,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Force + + Switch @@ -47908,6 +48358,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertG + + Switch @@ -47920,6 +48372,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertH + + Switch @@ -47932,6 +48386,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour InvertR + + Switch @@ -47944,6 +48400,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour NoResponse + + Switch @@ -47956,6 +48414,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour OffsetHeight + + Switch @@ -47968,6 +48428,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour PassThru + + Switch @@ -47980,6 +48442,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour ShaderText + + String @@ -47992,6 +48456,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour SourceName + + String @@ -48004,6 +48470,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Strength + + Float @@ -48016,6 +48484,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour Type + + Int @@ -48028,6 +48498,8 @@ Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-Sour UseShaderTime + + Switch @@ -53207,9 +53679,7 @@ This can increase performance, and also silently ignore critical errorsOBSQuadrilateralCropShader Get - -Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <float>] [[-TopRightX] <float>] [[-TopRightY] <float>] [[-BottomLeftX] <float>] [[-BottomLeftY] <float>] [[-BottomRightX] <float>] [[-BottomRightY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <float>] [[-TopRightX] <float>] [[-TopRightY] <float>] [[-BottomLeftX] <float>] [[-BottomLeftY] <float>] [[-BottomRightX] <float>] [[-BottomRightY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -53221,6 +53691,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopLeftX + + Float @@ -53233,6 +53705,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopLeftY + + Float @@ -53245,6 +53719,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopRightX + + Float @@ -53257,6 +53733,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopRightY + + Float @@ -53269,6 +53747,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomLeftX + + Float @@ -53281,6 +53761,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomLeftY + + Float @@ -53293,6 +53775,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomRightX + + Float @@ -53305,6 +53789,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomRightY + + Float @@ -53317,6 +53803,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa SourceName + + String @@ -53329,6 +53817,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa FilterName + + String @@ -53341,6 +53831,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa ShaderText + + String @@ -53353,6 +53845,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa Force + + Switch @@ -53365,6 +53859,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa PassThru + + Switch @@ -53377,6 +53873,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa NoResponse + + Switch @@ -53389,6 +53887,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa UseShaderTime + + Switch @@ -53404,6 +53904,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomLeftX + + Float @@ -53416,6 +53918,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomLeftY + + Float @@ -53428,6 +53932,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomRightX + + Float @@ -53440,6 +53946,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa BottomRightY + + Float @@ -53452,6 +53960,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa FilterName + + String @@ -53464,6 +53974,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa Force + + Switch @@ -53476,6 +53988,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa NoResponse + + Switch @@ -53488,6 +54002,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa PassThru + + Switch @@ -53500,6 +54016,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa ShaderText + + String @@ -53512,6 +54030,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa SourceName + + String @@ -53524,6 +54044,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopLeftX + + Float @@ -53536,6 +54058,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopLeftY + + Float @@ -53548,6 +54072,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopRightX + + Float @@ -53560,6 +54086,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa TopRightY + + Float @@ -53572,6 +54100,8 @@ Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <floa UseShaderTime + + Switch @@ -78153,9 +78683,7 @@ This can increase performance, and also silently ignore critical errorsOBSZoomBlurTransitionShader Get - -Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - + Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] 0.2.0.1 @@ -78167,6 +78695,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ImageA + + String @@ -78179,6 +78709,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ImageB + + String @@ -78191,6 +78723,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string TransitionTime + + Float @@ -78203,6 +78737,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ConvertLinear + + Switch @@ -78215,6 +78751,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string Strength + + Float @@ -78227,6 +78765,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string SourceName + + String @@ -78239,6 +78779,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string FilterName + + String @@ -78251,6 +78793,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ShaderText + + String @@ -78263,6 +78807,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string Force + + Switch @@ -78275,6 +78821,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string PassThru + + Switch @@ -78287,6 +78835,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string NoResponse + + Switch @@ -78299,6 +78849,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string UseShaderTime + + Switch @@ -78314,6 +78866,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ConvertLinear + + Switch @@ -78326,6 +78880,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string FilterName + + String @@ -78338,6 +78894,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string Force + + Switch @@ -78350,6 +78908,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ImageA + + String @@ -78362,6 +78922,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ImageB + + String @@ -78374,6 +78936,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string NoResponse + + Switch @@ -78386,6 +78950,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string PassThru + + Switch @@ -78398,6 +78964,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string ShaderText + + String @@ -78410,6 +78978,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string SourceName + + String @@ -78422,6 +78992,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string Strength + + Float @@ -78434,6 +79006,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string TransitionTime + + Float @@ -78446,6 +79020,8 @@ Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string UseShaderTime + + Switch @@ -95463,6 +96039,257 @@ This can increase performance, and also silently ignore critical errors + + + Stop-OBS + OBS + Stop + + Stops OBS + + 0.2.0.1 + + + Stops OBS. + By default, stops recording and streaming. + If -Process is provided, will stop all running OBS processes + If -Recording is provided, will stop recording + If -Streaming if provided, will stop obs streaming + If -VirtualCamera is provided, will stop the virtual camera + + + + Stop-OBS + + Recording + + If set, will stop recording. + + Switch + + Switch + + + + + + + Streaming + + If set, will stop streaming + + Switch + + Switch + + + + + + + VirtualCamera + + If set, will stop the virtual camera. + + Switch + + Switch + + + + + + + Process + + If set, will stop the OBS process. + + Switch + + Switch + + + + + + + StudioMode + + If set, will enable studio mode. + + Switch + + Switch + + + + + + + + + + Process + + If set, will stop the OBS process. + + Switch + + Switch + + + + + + + Recording + + If set, will stop recording. + + Switch + + Switch + + + + + + + Streaming + + If set, will stop streaming + + Switch + + Switch + + + + + + + StudioMode + + If set, will enable studio mode. + + Switch + + Switch + + + + + + + VirtualCamera + + If set, will stop the virtual camera. + + Switch + + Switch + + + + + + + + + + + This command Supports Should Process and has a ConfirmImpact of 'High' + +In an interactive session, this command prompt by default. + +If `-WhatIf` is passed, will output what happen if this ran +If `-Confirm:$false`, confirmation will be skipped. + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS > + + # Stop Streaming and Recording +Stop-OBS + + + + + -------------------------- EXAMPLE 2 -------------------------- + + PS > + + # Stops obs recording +Stop-OBS -Recording + + + + + -------------------------- EXAMPLE 3 -------------------------- + + PS > + + # Stop Streaming +Stop-OBS -Streaming + + + + + -------------------------- EXAMPLE 4 -------------------------- + + PS > + + # Stop OBS Virtual Camera +Stop-OBS -VirtualCamera + + + + + -------------------------- EXAMPLE 5 -------------------------- + + PS > + + # Stop OBS Virtual Camera without prompting +Stop-OBS -VirtualCamera -Confirm:$false + + + + + -------------------------- EXAMPLE 6 -------------------------- + + PS > + + Stop-OBS -StudioMode + + + + + + + Stop-OBSRecord + + + + + Stop-OBSStream + + + + + Stop-OBSVirtualCam + + + + + Set-OBSStudioModeEnabled + + + + + Stop-OBSEffect From 72c9d1ae387e1152519c14fdc665917fdd8aa85d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:22:08 +0000 Subject: [PATCH 250/266] feat(Stop-OBS): Adding Stop-OBS ( Fixes #226 ) --- allcommands.ps1 | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/allcommands.ps1 b/allcommands.ps1 index b6bda071..cc31a688 100644 --- a/allcommands.ps1 +++ b/allcommands.ps1 @@ -836,6 +836,112 @@ function Show-OBS { } } } +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBS { + + [CmdletBinding( + ConfirmImpact='High', + PositionalBinding=$false, + SupportsShouldProcess + )] + param( + # If set, will stop recording. + [switch] + $Recording, + + # If set, will stop streaming + [switch] + $Streaming, + + # If set, will stop the virtual camera. + [switch] + $VirtualCamera, + + # If set, will stop the OBS process. + [switch] + $Process, + + # If set, will enable studio mode. + [switch] + $StudioMode + ) + + if ($Process) { + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand('obs', 'Application') + if (-not $obsApp -and -not ($IsMacOS -or $IsLinux)) { + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand( + ( + Join-Path $env:ProgramFiles "obs-studio" | + Join-Path -ChildPath 'bin' | + Join-Path -ChildPath '64bit' | + Join-Path -ChildPath 'obs64.exe' + ), + 'Application' + ) + } + + if (-not $obsApp) { + Write-Error "OBS not found" + return + } + + $obsRunning = Get-Process | + Where-Object Path -EQ $obsApp.Source + + if (-not $obsRunning) { + Write-Error "OBS not running" + return + } + + if ($WhatIfPreference) { return $obsRunning } + if ($PSCmdlet.ShouldProcess('Stop OBS')) { + $obsRunning | Stop-Process -PassThru + } + return + } + + # Without any parameters, we will stop + if (-not $PSBoundParameters.Count) { + # recording + $Recording = $true + # and streaming. + $Streaming = $true + } + + + + # If we want to stop recording, + if ($Recording -and $PSCmdlet.ShouldProcess('Stop Recording')) { + # `Stop-ObsRecord`. + Stop-OBSRecord -PassThru:$WhatIfPreference + } + + # If we want to stop the virtual camera, + if ($VirtualCamera -and $PSCmdlet.ShouldProcess('Stop Virtual Camera')) { + # `Stop-OBSVirtualCamera`. + Stop-OBSVirtualCam -PassThru:$WhatIfPreference + } + + # If we want to stop streaming, + if ($Streaming -and $PSCmdlet.ShouldProcess('Stop Streaming')) { + # `Stop-OBSStream`. + Stop-OBSStream -PassThru:$WhatIfPreference + } + + if ($StudioMode -and $PSCmdlet.ShouldProcess('Stop Studio Mode')) { + Set-OBSStudioModeEnabled -StudioModeEnabled:$false -PassThru:$WhatIfPreference + } + + # If we are shutting down any part of obs + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera) { + # we do not want to stop obs + return + } +} + #.ExternalHelp obs-powershell-Help.xml function Watch-OBS { From 84ac1ad098cb0bd2b5729a653d62d035e785bb70 Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Fri, 22 May 2026 10:39:57 -0700 Subject: [PATCH 251/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) --- Commands/Start-OBS.ps1 | 156 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Commands/Start-OBS.ps1 diff --git a/Commands/Start-OBS.ps1 b/Commands/Start-OBS.ps1 new file mode 100644 index 00000000..190b83e6 --- /dev/null +++ b/Commands/Start-OBS.ps1 @@ -0,0 +1,156 @@ + +function Start-OBS { + <# + .SYNOPSIS + Start OBS + .DESCRIPTION + Starts OBS + + Without any parameters, will attempt to start the obs process. + + If OBS is already running, will output the current obs process. + + * If `-Recording` is passed, will start recording + * If `-Streaming` is passed, will start streaming + * If `-StudioMode` is passed, will start studio mode + * If `-VirtualCamera` is passed, will start the virtual camera + + If additional arguments are passed, will pass them thru to a new obs process. + .LINK + Stop-OBS + .LINK + Start-OBSRecord + .LINK + Start-OBSStream + .LINK + Start-OBSVirtualCam + #> + [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess)] + param( + # A list of arguments. These will be passed to a new obs process. + [Parameter(ValueFromRemainingArguments)] + [Alias('Arguments','Argument','Args')] + [PSObject[]] + $ArgumentList, + + # Any input object. This will currently be ignored. + [Parameter(ValueFromPipeline)] + [PSObject[]] + $InputObject, + + # If set, will start recording. + [Alias('Record')] + [switch] + $Recording, + + # If set, will start streaming. + [Alias('Stream')] + [switch] + $Streaming, + + # If set, will start studio mode. + [switch] + $StudioMode, + + # If set, will start the virtual camera + [Alias('VirtualCam')] + [switch] + $VirtualCamera + ) + + + # Get the obs application + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand('obs', 'Application') + # If it was not in the path, and we are on Windows + if (-not $obsApp -and -not ($IsMacOS -or $IsLinux)) { + # look in program files. + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand( + ( + Join-Path $env:ProgramFiles "obs-studio" | + Join-Path -ChildPath 'bin' | + Join-Path -ChildPath '64bit' | + Join-Path -ChildPath 'obs64.exe' + ), + 'Application' + ) + } + + # If we could not find obs + if (-not $obsApp) { + # error out + Write-Error "OBS not found" + return + } + + # Determine if obs is already running + $obsRunning = Get-Process | + Where-Object Path -EQ $obsApp.Source + + # If it is running, we can start various obs features. + if ($obsRunning) { + # If we want to start recording, + if ($Recording -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Recording') + ) { + # `Start-OBSRecord`. + Start-OBSRecord -PassThru:$WhatIfPreference + } + + # If we want to start streaming, + if ($Streaming -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Streaming') + )) { + # `Start-OBSStream`. + Start-OBSStream -PassThru:$WhatIfPreference + } + + # If we want to start studio mode, + if ($StudioMode -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Studio Mode') + )) { + # `Set-OBSStudioModeEnabled`. + Set-OBSStudioModeEnabled -StudioModeEnabled:$true -PassThru:$WhatIfPreference + } + + # If we want to start the virtual camera, + if ($VirtualCamera -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Virtual Camera') + )) { + # `Start-OBSVirtualCam`. + Start-OBSVirtualCam -PassThru:$WhatIfPreference + } + + # If any of these options were run + # they will output + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera + ) { + # and we can return. + return + } + + # Otherwise, return the obs process. + return $obsRunning + } else { + # If the process was not running, start it. + Start-Process -FilePath $obsApp.Path -PassThru -ArgumentList $ArgumentList -WorkingDirectory ( + $obsApp.Path | Split-Path + ) + + # If we wanted to start recording, streaming, studio mode, or virtual camera + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera + ) { + # warn us that we need to wait a bit. + Write-Warning "OBS starting up, cannot start virtual camera, recording, or streaming" + } + } +} \ No newline at end of file From 6936b838ed187286fedf913d80ebf406129de8a7 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:42:04 +0000 Subject: [PATCH 252/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) --- en-us/obs-powershell-commands.help.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/en-us/obs-powershell-commands.help.txt b/en-us/obs-powershell-commands.help.txt index e002e42a..07123a80 100644 --- a/en-us/obs-powershell-commands.help.txt +++ b/en-us/obs-powershell-commands.help.txt @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 838 commands -(333 functions and 505 aliases) +obs-powershell exports 839 commands +(334 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](docs/obs-powershell-websocket-commands.md). @@ -333,6 +333,7 @@ Functions |[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | |[Start-OBSStream](docs/Start-OBSStream.md) | |[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | +|[Stop-OBS](docs/Stop-OBS.md) | |[Stop-OBSEffect](docs/Stop-OBSEffect.md) | |[Stop-OBSOutput](docs/Stop-OBSOutput.md) | |[Stop-OBSRecord](docs/Stop-OBSRecord.md) | @@ -675,6 +676,7 @@ Aliases |[Start-OBSReplayBuffer](docs/Start-OBSReplayBuffer.md) | |[Start-OBSStream](docs/Start-OBSStream.md) | |[Start-OBSVirtualCam](docs/Start-OBSVirtualCam.md) | +|[Stop-OBS](docs/Stop-OBS.md) | |[Stop-OBSEffect](docs/Stop-OBSEffect.md) | |[Stop-OBSOutput](docs/Stop-OBSOutput.md) | |[Stop-OBSRecord](docs/Stop-OBSRecord.md) | From b51fd13903736f8761cf2478c2488fa962394063 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:42:04 +0000 Subject: [PATCH 253/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) --- obs-powershell.psd1 | 1 + 1 file changed, 1 insertion(+) diff --git a/obs-powershell.psd1 b/obs-powershell.psd1 index 6549e3ed..3c904695 100644 --- a/obs-powershell.psd1 +++ b/obs-powershell.psd1 @@ -103,6 +103,7 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Remove-OBS', 'Send-OBS', 'Show-OBS', +'Start-OBS', 'Stop-OBS', 'Watch-OBS', 'Set-OBS3DFilter', From 74b60e8ff4a43edd16087f8117ab21e7822b8c67 Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Fri, 22 May 2026 10:50:30 -0700 Subject: [PATCH 254/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- Commands/Start-OBS.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/Start-OBS.ps1 b/Commands/Start-OBS.ps1 index 190b83e6..c3ff1296 100644 --- a/Commands/Start-OBS.ps1 +++ b/Commands/Start-OBS.ps1 @@ -92,7 +92,7 @@ function Start-OBS { if ($Recording -and ( $WhatIfPreference -or $PSCmdlet.ShouldProcess('Start Recording') - ) { + )) { # `Start-OBSRecord`. Start-OBSRecord -PassThru:$WhatIfPreference } From 6badd204b956f1530b5ffd7c9f6bd8277407e905 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:53:24 +0000 Subject: [PATCH 255/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- docs/Start-OBS.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/Start-OBS.md diff --git a/docs/Start-OBS.md b/docs/Start-OBS.md new file mode 100644 index 00000000..05d4e87f --- /dev/null +++ b/docs/Start-OBS.md @@ -0,0 +1,96 @@ +Start-OBS +--------- + +### Synopsis +Start OBS + +--- + +### Description + +Starts OBS + +Without any parameters, will attempt to start the obs process. + +If OBS is already running, will output the current obs process. + +* If `-Recording` is passed, will start recording +* If `-Streaming` is passed, will start streaming +* If `-StudioMode` is passed, will start studio mode +* If `-VirtualCamera` is passed, will start the virtual camera + +If additional arguments are passed, will pass them thru to a new obs process. + +--- + +### Related Links +* [Stop-OBS](Stop-OBS.md) + +* [Start-OBSRecord](Start-OBSRecord.md) + +* [Start-OBSStream](Start-OBSStream.md) + +* [Start-OBSVirtualCam](Start-OBSVirtualCam.md) + +--- + +### Parameters +#### **ArgumentList** +A list of arguments. These will be passed to a new obs process. + +|Type |Required|Position|PipelineInput|Aliases | +|--------------|--------|--------|-------------|-------------------------------| +|`[PSObject[]]`|false |named |false |Arguments
Argument
Args| + +#### **InputObject** +Any input object. This will currently be ignored. + +|Type |Required|Position|PipelineInput | +|--------------|--------|--------|--------------| +|`[PSObject[]]`|false |named |true (ByValue)| + +#### **Recording** +If set, will start recording. + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[Switch]`|false |named |false |Record | + +#### **Streaming** +If set, will start streaming. + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[Switch]`|false |named |false |Stream | + +#### **StudioMode** +If set, will start studio mode. + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + +#### **VirtualCamera** +If set, will start the virtual camera + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|----------| +|`[Switch]`|false |named |false |VirtualCam| + +#### **WhatIf** +-WhatIf is an automatic variable that is created when a command has ```[CmdletBinding(SupportsShouldProcess)]```. +-WhatIf is used to see what would happen, or return operations without executing them +#### **Confirm** +-Confirm is an automatic variable that is created when a command has ```[CmdletBinding(SupportsShouldProcess)]```. +-Confirm is used to -Confirm each operation. + +If you pass ```-Confirm:$false``` you will not be prompted. + +If the command sets a ```[ConfirmImpact("Medium")]``` which is lower than ```$confirmImpactPreference```, you will not be prompted unless -Confirm is passed. + +--- + +### Syntax +```PowerShell +Start-OBS [-ArgumentList ] [-InputObject ] [-Recording] [-Streaming] [-StudioMode] [-VirtualCamera] [-WhatIf] [-Confirm] [] +``` From 541837f967fa33b4238c14e1cb53bd39a5a1c4cd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:53:24 +0000 Subject: [PATCH 256/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- docs/_data/Help/Start-OBS.json | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/_data/Help/Start-OBS.json diff --git a/docs/_data/Help/Start-OBS.json b/docs/_data/Help/Start-OBS.json new file mode 100644 index 00000000..8b1741d8 --- /dev/null +++ b/docs/_data/Help/Start-OBS.json @@ -0,0 +1,38 @@ +{ + "Synopsis": "Start OBS", + "Description": "Starts OBS\n\nWithout any parameters, will attempt to start the obs process.\n\nIf OBS is already running, will output the current obs process.\n\n* If `-Recording` is passed, will start recording\n* If `-Streaming` is passed, will start streaming\n* If `-StudioMode` is passed, will start studio mode\n* If `-VirtualCamera` is passed, will start the virtual camera\n\nIf additional arguments are passed, will pass them thru to a new obs process.", + "Parameters": [ + { + "Name": null, + "Type": null, + "Description": "", + "Required": false, + "Position": 0, + "Aliases": null, + "DefaultValue": null, + "Globbing": false, + "PipelineInput": null, + "variableLength": false + } + ], + "Notes": [ + null + ], + "CommandType": "Function", + "Component": [ + null + ], + "Inputs": [ + null + ], + "Outputs": [ + null + ], + "Links": [ + null, + null, + null, + null + ], + "Examples": [] +} \ No newline at end of file From f63889667727bded9ba5a60c468d968cc9ad871e Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:54:36 +0000 Subject: [PATCH 257/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- docs/obs-powershell-commands.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/obs-powershell-commands.md b/docs/obs-powershell-commands.md index 4c2f15e3..7104d20f 100644 --- a/docs/obs-powershell-commands.md +++ b/docs/obs-powershell-commands.md @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 838 commands -(333 functions and 505 aliases) +obs-powershell exports 839 commands +(334 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](obs-powershell-websocket-commands.md). @@ -333,6 +333,7 @@ Functions |[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | |[Start-OBSStream](Start-OBSStream.md) | |[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | +|[Stop-OBS](Stop-OBS.md) | |[Stop-OBSEffect](Stop-OBSEffect.md) | |[Stop-OBSOutput](Stop-OBSOutput.md) | |[Stop-OBSRecord](Stop-OBSRecord.md) | @@ -675,6 +676,7 @@ Aliases |[Start-OBSReplayBuffer](Start-OBSReplayBuffer.md) | |[Start-OBSStream](Start-OBSStream.md) | |[Start-OBSVirtualCam](Start-OBSVirtualCam.md) | +|[Stop-OBS](Stop-OBS.md) | |[Stop-OBSEffect](Stop-OBSEffect.md) | |[Stop-OBSOutput](Stop-OBSOutput.md) | |[Stop-OBSRecord](Stop-OBSRecord.md) | From 8fe4ca4108039f4784f090da4df1fe90958c4776 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:54:47 +0000 Subject: [PATCH 258/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- obs-powershell-Help.xml | 206 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/obs-powershell-Help.xml b/obs-powershell-Help.xml index 3c72ec4f..792e9b33 100644 --- a/obs-powershell-Help.xml +++ b/obs-powershell-Help.xml @@ -95183,6 +95183,212 @@ Show-OBS -FilePath .\BlueRect.svg
+ + + Start-OBS + OBS + Start + + Start OBS + + 0.2.0.1 + + + Starts OBS + Without any parameters, will attempt to start the obs process. + If OBS is already running, will output the current obs process. + * If `-Recording` is passed, will start recording + * If `-Streaming` is passed, will start streaming + * If `-StudioMode` is passed, will start studio mode + * If `-VirtualCamera` is passed, will start the virtual camera + If additional arguments are passed, will pass them thru to a new obs process. + + + + Start-OBS + + ArgumentList + + A list of arguments. These will be passed to a new obs process. + + System.Management.Automation.PSObject[] + + System.Management.Automation.PSObject[] + + + + + + + InputObject + + Any input object. This will currently be ignored. + + System.Management.Automation.PSObject[] + + System.Management.Automation.PSObject[] + + + + + + + Recording + + If set, will start recording. + + Switch + + Switch + + + + + + + Streaming + + If set, will start streaming. + + Switch + + Switch + + + + + + + StudioMode + + If set, will start studio mode. + + Switch + + Switch + + + + + + + VirtualCamera + + If set, will start the virtual camera + + Switch + + Switch + + + + + + + + + + ArgumentList + + A list of arguments. These will be passed to a new obs process. + + System.Management.Automation.PSObject[] + + System.Management.Automation.PSObject[] + + + + + + + InputObject + + Any input object. This will currently be ignored. + + System.Management.Automation.PSObject[] + + System.Management.Automation.PSObject[] + + + + + + + Recording + + If set, will start recording. + + Switch + + Switch + + + + + + + Streaming + + If set, will start streaming. + + Switch + + Switch + + + + + + + StudioMode + + If set, will start studio mode. + + Switch + + Switch + + + + + + + VirtualCamera + + If set, will start the virtual camera + + Switch + + Switch + + + + + + + + + Stop-OBS + + + + + Start-OBSRecord + + + + + Start-OBSStream + + + + + Start-OBSVirtualCam + + + + + Start-OBSEffect From 097141d5115de34c5edbecc1ee9810f0660e760a Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Fri, 22 May 2026 17:54:47 +0000 Subject: [PATCH 259/266] feat(Start-OBS): Adding Start-OBS ( Fixes #220 ) Balancing --- allcommands.ps1 | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/allcommands.ps1 b/allcommands.ps1 index cc31a688..d7856da5 100644 --- a/allcommands.ps1 +++ b/allcommands.ps1 @@ -836,6 +836,137 @@ function Show-OBS { } } } +function Start-OBS { + + [CmdletBinding(PositionalBinding=$false,SupportsShouldProcess)] + param( + # A list of arguments. These will be passed to a new obs process. + [Parameter(ValueFromRemainingArguments)] + [Alias('Arguments','Argument','Args')] + [PSObject[]] + $ArgumentList, + + # Any input object. This will currently be ignored. + [Parameter(ValueFromPipeline)] + [PSObject[]] + $InputObject, + + # If set, will start recording. + [Alias('Record')] + [switch] + $Recording, + + # If set, will start streaming. + [Alias('Stream')] + [switch] + $Streaming, + + # If set, will start studio mode. + [switch] + $StudioMode, + + # If set, will start the virtual camera + [Alias('VirtualCam')] + [switch] + $VirtualCamera + ) + + + # Get the obs application + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand('obs', 'Application') + # If it was not in the path, and we are on Windows + if (-not $obsApp -and -not ($IsMacOS -or $IsLinux)) { + # look in program files. + $obsApp = $ExecutionContext.SessionState.InvokeCommand.GetCommand( + ( + Join-Path $env:ProgramFiles "obs-studio" | + Join-Path -ChildPath 'bin' | + Join-Path -ChildPath '64bit' | + Join-Path -ChildPath 'obs64.exe' + ), + 'Application' + ) + } + + # If we could not find obs + if (-not $obsApp) { + # error out + Write-Error "OBS not found" + return + } + + # Determine if obs is already running + $obsRunning = Get-Process | + Where-Object Path -EQ $obsApp.Source + + # If it is running, we can start various obs features. + if ($obsRunning) { + # If we want to start recording, + if ($Recording -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Recording') + )) { + # `Start-OBSRecord`. + Start-OBSRecord -PassThru:$WhatIfPreference + } + + # If we want to start streaming, + if ($Streaming -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Streaming') + )) { + # `Start-OBSStream`. + Start-OBSStream -PassThru:$WhatIfPreference + } + + # If we want to start studio mode, + if ($StudioMode -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Studio Mode') + )) { + # `Set-OBSStudioModeEnabled`. + Set-OBSStudioModeEnabled -StudioModeEnabled:$true -PassThru:$WhatIfPreference + } + + # If we want to start the virtual camera, + if ($VirtualCamera -and ( + $WhatIfPreference -or + $PSCmdlet.ShouldProcess('Start Virtual Camera') + )) { + # `Start-OBSVirtualCam`. + Start-OBSVirtualCam -PassThru:$WhatIfPreference + } + + # If any of these options were run + # they will output + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera + ) { + # and we can return. + return + } + + # Otherwise, return the obs process. + return $obsRunning + } else { + # If the process was not running, start it. + Start-Process -FilePath $obsApp.Path -PassThru -ArgumentList $ArgumentList -WorkingDirectory ( + $obsApp.Path | Split-Path + ) + + # If we wanted to start recording, streaming, studio mode, or virtual camera + if ($Recording -or + $Streaming -or + $StudioMode -or + $VirtualCamera + ) { + # warn us that we need to wait a bit. + Write-Warning "OBS starting up, cannot start virtual camera, recording, or streaming" + } + } +} #.ExternalHelp obs-powershell-Help.xml function Stop-OBS { From b102ff30351cedb7b74547b9b7215fb8c11cda32 Mon Sep 17 00:00:00 2001 From: James Brundage <+@noreply.github.com> Date: Mon, 1 Jun 2026 11:50:36 -0700 Subject: [PATCH 260/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- CHANGELOG.md | 26 +++++++++++ obs-powershell.ps.psd1 | 99 ++++++++++++------------------------------ 2 files changed, 53 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6f75620..54f80ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,32 @@ > Like It? [Star It](https://github.com/StartAutomating/obs-powershell) > Love It? [Support It](https://github.com/sponsors/StartAutomating) +## obs-powershell 0.2.1: + +* New General Purpose Commands + * Start-OBS (#220) + * Stop-OBS (#226) +* New Shader Commands: + * Get-OBS3dPanelShader + * Get-OBSAudioShader + * Get-OBSCubeRotatingShader + * Get-OBSDisplacementMapAdvancedInvertShader + * Get-OBSDisplacementMapAdvancedShader + * Get-OBSDisplacementMapInvertShader + * Get-OBSDisplacementMapShader + * Get-OBSGlitchPeriodicShader + * Get-OBSHardBlinkShader + * Get-OBSMotionBlurShader + * Get-OBSNoiseShader + * Get-OBSNormalMapShader + * Get-OBSPerspectiveShader + * Get-OBSQuadrilateralCropShader + * Get-OBSRepeatGridCenterCropShader + * Get-OBSWalkingDeadPixelFixerShader + * Get-OBSZoomBlurTransitionShader + +--- + ## obs-powershell 0.2.0.1: * Fixing `Watch-OBS` (Fixes #216) diff --git a/obs-powershell.ps.psd1 b/obs-powershell.ps.psd1 index 5f93dac7..176f454f 100644 --- a/obs-powershell.ps.psd1 +++ b/obs-powershell.ps.psd1 @@ -1,11 +1,11 @@ @{ - ModuleVersion = '0.2.0.1' + ModuleVersion = '0.2.1' RootModule = 'obs-powershell.psm1' Description = 'Script your streams' Guid = '1417123e-a932-439f-9b68-a7313cf1e170' Author = 'James Brundage' CompanyName = 'Start-Automating' - Copyright = '2022-2025 Start-Automating' + Copyright = '2022-2026 Start-Automating' FormatsToProcess = 'obs-powershell.format.ps1xml' TypesToProcess = 'obs-powershell.types.ps1xml' PowerShellVersion = '7.0' @@ -15,82 +15,37 @@ ProjectURI = 'https://github.com/StartAutomating/obs-powershell' LicenseURI = 'https://github.com/StartAutomating/obs-powershell/blob/main/LICENSE' ReleaseNotes = @' -> Like It? [Star It](https://github.com/StartAutomating/obs-powershell) -> Love It? [Support It](https://github.com/sponsors/StartAutomating) +## obs-powershell 0.2.1: -## obs-powershell 0.2.0.1: - -* Fixing `Watch-OBS` (Fixes #216) -* Adding `CONTRIBUTING.md` (Fixes #204) -* Adding `CODE_OF_CONDUCT.md` (Fixes #205) +* New General Purpose Commands + * Start-OBS (#220) + * Stop-OBS (#226) +* New Shader Commands: + * Get-OBS3dPanelShader + * Get-OBSAudioShader + * Get-OBSCubeRotatingShader + * Get-OBSDisplacementMapAdvancedInvertShader + * Get-OBSDisplacementMapAdvancedShader + * Get-OBSDisplacementMapInvertShader + * Get-OBSDisplacementMapShader + * Get-OBSGlitchPeriodicShader + * Get-OBSHardBlinkShader + * Get-OBSMotionBlurShader + * Get-OBSNoiseShader + * Get-OBSNormalMapShader + * Get-OBSPerspectiveShader + * Get-OBSQuadrilateralCropShader + * Get-OBSRepeatGridCenterCropShader + * Get-OBSWalkingDeadPixelFixerShader + * Get-OBSZoomBlurTransitionShader --- -## obs-powershell 0.2: - -* So Many Shaders! -* @exeldro makes some excellent obs plugins - * Every PixelShader from [obs-shaderfilter](https://github.com/exeldro/obs-shaderfilter) has an auto-generated function: - * As of this build, there are 142 Shader functions! - * Flip Shader ( #200 ) - * Zoom XY Shader ( #199 ) - * RGBA Percent Shader ( #198 ) - * Reflect Shader ( #197 ) - * Shader Commands now support -Force -* Drastically improved start time on Windows (#214) -* OBS Sources: - * New Sources: - * OBSSoundCloudSource ( #179 ) - * OBSSwitchSource (#142) - * OBSMarkdownSource (#143) - * OBSWaveformSource (#141) - * All existing sources are now implemented in a `Get`, and aliased to `Set`,`Add` - * Making Set also Get-OBSWindowSource (#152) - * Making Set also Get-OBSVLCSource (#151) - * Making Set also Get-OBSMediaSource (#150) - * Making Set also Get-OBSColorSource (#148) - * Making Set also Get-OBSBrowserSource (#147) - * Making Set also Get-OBSAudioOutputSource (#146) -* New Effects: - * Zoom In / Out Effect ( #164 ) - * Start-OBSEffect - Adding -Reverse (Fixes #121) -* Exporting `$obs` (#157, #158, #159) and drastically expanding pseudo types -* Pseudo Types - * GetCurrentProgramScene.ToString() ( Fixes #202, Fixes #166 ) - * OBS.Beat ( #195 ) - * OBS.Beat.Timer - * OBS.Beat.TapBPM ( #191) - * Stopping OBS.Beat.Timer on Unload - * OBS.Beat.get_Sine ( #192 ) - * OBS.Beat.get_Cosine ( #193 ) - * OBS.Beat.Angle ( #194 ) - * OBS.Beat.Duration ( #189 ) - * OBS.Beat.BeatCount ( #190 ) - * OBS.Beat.BeatStart ( #188 ) - * OBS.Beat.BPM ( #187 ) - * $obs.Beat ( #186 ) - * OBS.Input - * OBS.Input.Disable/EnableAllFilter(s) ( #183 ) - * OBS.SceneItem.Animate Permissiveness ( #182 ) - * OBS.Filter.Disable PassThru support ( #181 ) - * OBS.Statistics ( #178 ) - * OBS.Input ( #174 ) - * OBS.Filter ( #175 ) - * OBS.SceneItem ( #173 ) - * OBS.GetSceneItemList.Response.Stretch() ( #172 ) - * OBS.GetSceneItemList.Response.Center() ( #171 ) - * OBS.GetInputList .SourceName alias ( #170 ) - * Adding .SceneItem to OBS.Inputs (Fixes #154) -* Minor Fixes: - * Watch-OBS -BufferSize: Defaulting to 64kb ( Fixes #212, Fixes #213 ) - * Fixing -Scene parameter defaults ( Fixes #210 ) - * Updating Build Conditions - * obs-powershell now mounts itself ( #180 ) - * obs-powershell supporting module profiles (#155) +> Like It? [Star It](https://github.com/StartAutomating/obs-powershell) +> Love It? [Support It](https://github.com/sponsors/StartAutomating) ---- -Previous release notes available in the [CHANGELOG](https://github.com/StartAutomating/obs-powershell/blob/main/CHANGELOG.md) +Additional History available in the [CHANGELOG](https://github.com/StartAutomating/obs-powershell/blob/main/CHANGELOG.md) '@ } } From 413343fe13906c79bdeff805925004dd6108f343 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:52:56 +0000 Subject: [PATCH 261/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- en-us/obs-powershell-commands.help.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/en-us/obs-powershell-commands.help.txt b/en-us/obs-powershell-commands.help.txt index 07123a80..f0131b8a 100644 --- a/en-us/obs-powershell-commands.help.txt +++ b/en-us/obs-powershell-commands.help.txt @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 839 commands -(334 functions and 505 aliases) +obs-powershell exports 840 commands +(335 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](docs/obs-powershell-websocket-commands.md). @@ -327,6 +327,7 @@ Functions |[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | |[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | |[Show-OBS](docs/Show-OBS.md) | +|[Start-OBS](docs/Start-OBS.md) | |[Start-OBSEffect](docs/Start-OBSEffect.md) | |[Start-OBSOutput](docs/Start-OBSOutput.md) | |[Start-OBSRecord](docs/Start-OBSRecord.md) | @@ -670,6 +671,7 @@ Aliases |[Set-OBSWaveformSource](docs/Set-OBSWaveformSource.md) | |[Set-OBSWindowSource](docs/Set-OBSWindowSource.md) | |[Show-OBS](docs/Show-OBS.md) | +|[Start-OBS](docs/Start-OBS.md) | |[Start-OBSEffect](docs/Start-OBSEffect.md) | |[Start-OBSOutput](docs/Start-OBSOutput.md) | |[Start-OBSRecord](docs/Start-OBSRecord.md) | From 9944064821e0365ee9279ad0c7ba410d19df5821 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:52:56 +0000 Subject: [PATCH 262/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- obs-powershell.psd1 | 391 ++++++++++++++++++++------------------------ 1 file changed, 173 insertions(+), 218 deletions(-) diff --git a/obs-powershell.psd1 b/obs-powershell.psd1 index 3c904695..8dfc1f77 100644 --- a/obs-powershell.psd1 +++ b/obs-powershell.psd1 @@ -1,11 +1,11 @@ @{ - ModuleVersion = '0.2.0.1' + ModuleVersion = '0.2.1' RootModule = 'obs-powershell.psm1' Description = 'Script your streams' Guid = '1417123e-a932-439f-9b68-a7313cf1e170' Author = 'James Brundage' CompanyName = 'Start-Automating' - Copyright = '2022-2025 Start-Automating' + Copyright = '2022-2026 Start-Automating' FormatsToProcess = 'obs-powershell.format.ps1xml' TypesToProcess = 'obs-powershell.types.ps1xml' PowerShellVersion = '7.0' @@ -15,82 +15,37 @@ ProjectURI = 'https://github.com/StartAutomating/obs-powershell' LicenseURI = 'https://github.com/StartAutomating/obs-powershell/blob/main/LICENSE' ReleaseNotes = @' -> Like It? [Star It](https://github.com/StartAutomating/obs-powershell) -> Love It? [Support It](https://github.com/sponsors/StartAutomating) - -## obs-powershell 0.2.0.1: +## obs-powershell 0.2.1: -* Fixing `Watch-OBS` (Fixes #216) -* Adding `CONTRIBUTING.md` (Fixes #204) -* Adding `CODE_OF_CONDUCT.md` (Fixes #205) +* New General Purpose Commands + * Start-OBS (#220) + * Stop-OBS (#226) +* New Shader Commands: + * Get-OBS3dPanelShader + * Get-OBSAudioShader + * Get-OBSCubeRotatingShader + * Get-OBSDisplacementMapAdvancedInvertShader + * Get-OBSDisplacementMapAdvancedShader + * Get-OBSDisplacementMapInvertShader + * Get-OBSDisplacementMapShader + * Get-OBSGlitchPeriodicShader + * Get-OBSHardBlinkShader + * Get-OBSMotionBlurShader + * Get-OBSNoiseShader + * Get-OBSNormalMapShader + * Get-OBSPerspectiveShader + * Get-OBSQuadrilateralCropShader + * Get-OBSRepeatGridCenterCropShader + * Get-OBSWalkingDeadPixelFixerShader + * Get-OBSZoomBlurTransitionShader --- -## obs-powershell 0.2: - -* So Many Shaders! -* @exeldro makes some excellent obs plugins - * Every PixelShader from [obs-shaderfilter](https://github.com/exeldro/obs-shaderfilter) has an auto-generated function: - * As of this build, there are 142 Shader functions! - * Flip Shader ( #200 ) - * Zoom XY Shader ( #199 ) - * RGBA Percent Shader ( #198 ) - * Reflect Shader ( #197 ) - * Shader Commands now support -Force -* Drastically improved start time on Windows (#214) -* OBS Sources: - * New Sources: - * OBSSoundCloudSource ( #179 ) - * OBSSwitchSource (#142) - * OBSMarkdownSource (#143) - * OBSWaveformSource (#141) - * All existing sources are now implemented in a `Get`, and aliased to `Set`,`Add` - * Making Set also Get-OBSWindowSource (#152) - * Making Set also Get-OBSVLCSource (#151) - * Making Set also Get-OBSMediaSource (#150) - * Making Set also Get-OBSColorSource (#148) - * Making Set also Get-OBSBrowserSource (#147) - * Making Set also Get-OBSAudioOutputSource (#146) -* New Effects: - * Zoom In / Out Effect ( #164 ) - * Start-OBSEffect - Adding -Reverse (Fixes #121) -* Exporting `$obs` (#157, #158, #159) and drastically expanding pseudo types -* Pseudo Types - * GetCurrentProgramScene.ToString() ( Fixes #202, Fixes #166 ) - * OBS.Beat ( #195 ) - * OBS.Beat.Timer - * OBS.Beat.TapBPM ( #191) - * Stopping OBS.Beat.Timer on Unload - * OBS.Beat.get_Sine ( #192 ) - * OBS.Beat.get_Cosine ( #193 ) - * OBS.Beat.Angle ( #194 ) - * OBS.Beat.Duration ( #189 ) - * OBS.Beat.BeatCount ( #190 ) - * OBS.Beat.BeatStart ( #188 ) - * OBS.Beat.BPM ( #187 ) - * $obs.Beat ( #186 ) - * OBS.Input - * OBS.Input.Disable/EnableAllFilter(s) ( #183 ) - * OBS.SceneItem.Animate Permissiveness ( #182 ) - * OBS.Filter.Disable PassThru support ( #181 ) - * OBS.Statistics ( #178 ) - * OBS.Input ( #174 ) - * OBS.Filter ( #175 ) - * OBS.SceneItem ( #173 ) - * OBS.GetSceneItemList.Response.Stretch() ( #172 ) - * OBS.GetSceneItemList.Response.Center() ( #171 ) - * OBS.GetInputList .SourceName alias ( #170 ) - * Adding .SceneItem to OBS.Inputs (Fixes #154) -* Minor Fixes: - * Watch-OBS -BufferSize: Defaulting to 64kb ( Fixes #212, Fixes #213 ) - * Fixing -Scene parameter defaults ( Fixes #210 ) - * Updating Build Conditions - * obs-powershell now mounts itself ( #180 ) - * obs-powershell supporting module profiles (#155) +> Like It? [Star It](https://github.com/StartAutomating/obs-powershell) +> Love It? [Support It](https://github.com/sponsors/StartAutomating) ---- -Previous release notes available in the [CHANGELOG](https://github.com/StartAutomating/obs-powershell/blob/main/CHANGELOG.md) +Additional History available in the [CHANGELOG](https://github.com/StartAutomating/obs-powershell/blob/main/CHANGELOG.md) '@ } } @@ -115,6 +70,151 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Set-OBSScrollFilter', 'Set-OBSShaderFilter', 'Set-OBSSharpnessFilter', +'Get-OBSEffect', +'Import-OBSEffect', +'Remove-OBSEffect', +'Start-OBSEffect', +'Stop-OBSEffect', +'Add-OBSInput', +'Add-OBSProfile', +'Add-OBSScene', +'Add-OBSSceneCollection', +'Add-OBSSceneItem', +'Add-OBSSourceFilter', +'Copy-OBSSceneItem', +'Get-OBSCurrentPreviewScene', +'Get-OBSCurrentProgramScene', +'Get-OBSCurrentSceneTransition', +'Get-OBSCurrentSceneTransitionCursor', +'Get-OBSGroup', +'Get-OBSGroupSceneItem', +'Get-OBSHotkey', +'Get-OBSInput', +'Get-OBSInputAudioBalance', +'Get-OBSInputAudioMonitorType', +'Get-OBSInputAudioSyncOffset', +'Get-OBSInputAudioTracks', +'Get-OBSInputDefaultSettings', +'Get-OBSInputKind', +'Get-OBSInputMute', +'Get-OBSInputPropertiesListPropertyItems', +'Get-OBSInputSettings', +'Get-OBSInputVolume', +'Get-OBSLastReplayBufferReplay', +'Get-OBSMediaInputStatus', +'Get-OBSMonitor', +'Get-OBSOutput', +'Get-OBSOutputSettings', +'Get-OBSOutputStatus', +'Get-OBSPersistentData', +'Get-OBSProfile', +'Get-OBSProfileParameter', +'Get-OBSRecordDirectory', +'Get-OBSRecordStatus', +'Get-OBSReplayBufferStatus', +'Get-OBSScene', +'Get-OBSSceneCollection', +'Get-OBSSceneItem', +'Get-OBSSceneItemBlendMode', +'Get-OBSSceneItemEnabled', +'Get-OBSSceneItemId', +'Get-OBSSceneItemIndex', +'Get-OBSSceneItemLocked', +'Get-OBSSceneItemSource', +'Get-OBSSceneItemTransform', +'Get-OBSSceneSceneTransitionOverride', +'Get-OBSSceneTransition', +'Get-OBSSourceActive', +'Get-OBSSourceFilter', +'Get-OBSSourceFilterDefaultSettings', +'Get-OBSSourceFilterKind', +'Get-OBSSourceFilterList', +'Get-OBSSourceScreenshot', +'Get-OBSSpecialInputs', +'Get-OBSStats', +'Get-OBSStreamServiceSettings', +'Get-OBSStreamStatus', +'Get-OBSStudioModeEnabled', +'Get-OBSTransitionKind', +'Get-OBSVersion', +'Get-OBSVideoSettings', +'Get-OBSVirtualCamStatus', +'Open-OBSInputFiltersDialog', +'Open-OBSInputInteractDialog', +'Open-OBSInputPropertiesDialog', +'Open-OBSSourceProjector', +'Open-OBSVideoMixProjector', +'Remove-OBSInput', +'Remove-OBSProfile', +'Remove-OBSScene', +'Remove-OBSSceneItem', +'Remove-OBSSourceFilter', +'Resume-OBSRecord', +'Save-OBSReplayBuffer', +'Save-OBSSourceScreenshot', +'Send-OBSCallVendorRequest', +'Send-OBSCustomEvent', +'Send-OBSOffsetMediaInputCursor', +'Send-OBSPauseRecord', +'Send-OBSPressInputPropertiesButton', +'Send-OBSSleep', +'Send-OBSStreamCaption', +'Send-OBSTriggerHotkeyByKeySequence', +'Send-OBSTriggerHotkeyByName', +'Send-OBSTriggerMediaInputAction', +'Send-OBSTriggerStudioModeTransition', +'Set-OBSCurrentPreviewScene', +'Set-OBSCurrentProfile', +'Set-OBSCurrentProgramScene', +'Set-OBSCurrentSceneCollection', +'Set-OBSCurrentSceneTransition', +'Set-OBSCurrentSceneTransitionDuration', +'Set-OBSCurrentSceneTransitionSettings', +'Set-OBSInputAudioBalance', +'Set-OBSInputAudioMonitorType', +'Set-OBSInputAudioSyncOffset', +'Set-OBSInputAudioTracks', +'Set-OBSInputMute', +'Set-OBSInputName', +'Set-OBSInputSettings', +'Set-OBSInputVolume', +'Set-OBSMediaInputCursor', +'Set-OBSOutputSettings', +'Set-OBSPersistentData', +'Set-OBSProfileParameter', +'Set-OBSRecordDirectory', +'Set-OBSSceneItemBlendMode', +'Set-OBSSceneItemEnabled', +'Set-OBSSceneItemIndex', +'Set-OBSSceneItemLocked', +'Set-OBSSceneItemTransform', +'Set-OBSSceneName', +'Set-OBSSceneSceneTransitionOverride', +'Set-OBSSourceFilterEnabled', +'Set-OBSSourceFilterIndex', +'Set-OBSSourceFilterName', +'Set-OBSSourceFilterSettings', +'Set-OBSStreamServiceSettings', +'Set-OBSStudioModeEnabled', +'Set-OBSTBarPosition', +'Set-OBSVideoSettings', +'Start-OBSOutput', +'Start-OBSRecord', +'Start-OBSReplayBuffer', +'Start-OBSStream', +'Start-OBSVirtualCam', +'Stop-OBSOutput', +'Stop-OBSRecord', +'Stop-OBSReplayBuffer', +'Stop-OBSStream', +'Stop-OBSVirtualCam', +'Switch-OBSInputMute', +'Switch-OBSOutput', +'Switch-OBSRecord', +'Switch-OBSRecordPause', +'Switch-OBSReplayBuffer', +'Switch-OBSStream', +'Switch-OBSVirtualCam', 'Get-OBS3dPanelShader', 'Get-OBS3dSwapTransitionShader', 'Get-OBSAddShader', @@ -283,151 +383,6 @@ Previous release notes available in the [CHANGELOG](https://github.com/StartAuto 'Set-OBSSwitchSource', 'Set-OBSVLCSource', 'Set-OBSWaveformSource', -'Set-OBSWindowSource', -'Add-OBSInput', -'Add-OBSProfile', -'Add-OBSScene', -'Add-OBSSceneCollection', -'Add-OBSSceneItem', -'Add-OBSSourceFilter', -'Copy-OBSSceneItem', -'Get-OBSCurrentPreviewScene', -'Get-OBSCurrentProgramScene', -'Get-OBSCurrentSceneTransition', -'Get-OBSCurrentSceneTransitionCursor', -'Get-OBSGroup', -'Get-OBSGroupSceneItem', -'Get-OBSHotkey', -'Get-OBSInput', -'Get-OBSInputAudioBalance', -'Get-OBSInputAudioMonitorType', -'Get-OBSInputAudioSyncOffset', -'Get-OBSInputAudioTracks', -'Get-OBSInputDefaultSettings', -'Get-OBSInputKind', -'Get-OBSInputMute', -'Get-OBSInputPropertiesListPropertyItems', -'Get-OBSInputSettings', -'Get-OBSInputVolume', -'Get-OBSLastReplayBufferReplay', -'Get-OBSMediaInputStatus', -'Get-OBSMonitor', -'Get-OBSOutput', -'Get-OBSOutputSettings', -'Get-OBSOutputStatus', -'Get-OBSPersistentData', -'Get-OBSProfile', -'Get-OBSProfileParameter', -'Get-OBSRecordDirectory', -'Get-OBSRecordStatus', -'Get-OBSReplayBufferStatus', -'Get-OBSScene', -'Get-OBSSceneCollection', -'Get-OBSSceneItem', -'Get-OBSSceneItemBlendMode', -'Get-OBSSceneItemEnabled', -'Get-OBSSceneItemId', -'Get-OBSSceneItemIndex', -'Get-OBSSceneItemLocked', -'Get-OBSSceneItemSource', -'Get-OBSSceneItemTransform', -'Get-OBSSceneSceneTransitionOverride', -'Get-OBSSceneTransition', -'Get-OBSSourceActive', -'Get-OBSSourceFilter', -'Get-OBSSourceFilterDefaultSettings', -'Get-OBSSourceFilterKind', -'Get-OBSSourceFilterList', -'Get-OBSSourceScreenshot', -'Get-OBSSpecialInputs', -'Get-OBSStats', -'Get-OBSStreamServiceSettings', -'Get-OBSStreamStatus', -'Get-OBSStudioModeEnabled', -'Get-OBSTransitionKind', -'Get-OBSVersion', -'Get-OBSVideoSettings', -'Get-OBSVirtualCamStatus', -'Open-OBSInputFiltersDialog', -'Open-OBSInputInteractDialog', -'Open-OBSInputPropertiesDialog', -'Open-OBSSourceProjector', -'Open-OBSVideoMixProjector', -'Remove-OBSInput', -'Remove-OBSProfile', -'Remove-OBSScene', -'Remove-OBSSceneItem', -'Remove-OBSSourceFilter', -'Resume-OBSRecord', -'Save-OBSReplayBuffer', -'Save-OBSSourceScreenshot', -'Send-OBSCallVendorRequest', -'Send-OBSCustomEvent', -'Send-OBSOffsetMediaInputCursor', -'Send-OBSPauseRecord', -'Send-OBSPressInputPropertiesButton', -'Send-OBSSleep', -'Send-OBSStreamCaption', -'Send-OBSTriggerHotkeyByKeySequence', -'Send-OBSTriggerHotkeyByName', -'Send-OBSTriggerMediaInputAction', -'Send-OBSTriggerStudioModeTransition', -'Set-OBSCurrentPreviewScene', -'Set-OBSCurrentProfile', -'Set-OBSCurrentProgramScene', -'Set-OBSCurrentSceneCollection', -'Set-OBSCurrentSceneTransition', -'Set-OBSCurrentSceneTransitionDuration', -'Set-OBSCurrentSceneTransitionSettings', -'Set-OBSInputAudioBalance', -'Set-OBSInputAudioMonitorType', -'Set-OBSInputAudioSyncOffset', -'Set-OBSInputAudioTracks', -'Set-OBSInputMute', -'Set-OBSInputName', -'Set-OBSInputSettings', -'Set-OBSInputVolume', -'Set-OBSMediaInputCursor', -'Set-OBSOutputSettings', -'Set-OBSPersistentData', -'Set-OBSProfileParameter', -'Set-OBSRecordDirectory', -'Set-OBSSceneItemBlendMode', -'Set-OBSSceneItemEnabled', -'Set-OBSSceneItemIndex', -'Set-OBSSceneItemLocked', -'Set-OBSSceneItemTransform', -'Set-OBSSceneName', -'Set-OBSSceneSceneTransitionOverride', -'Set-OBSSourceFilterEnabled', -'Set-OBSSourceFilterIndex', -'Set-OBSSourceFilterName', -'Set-OBSSourceFilterSettings', -'Set-OBSStreamServiceSettings', -'Set-OBSStudioModeEnabled', -'Set-OBSTBarPosition', -'Set-OBSVideoSettings', -'Start-OBSOutput', -'Start-OBSRecord', -'Start-OBSReplayBuffer', -'Start-OBSStream', -'Start-OBSVirtualCam', -'Stop-OBSOutput', -'Stop-OBSRecord', -'Stop-OBSReplayBuffer', -'Stop-OBSStream', -'Stop-OBSVirtualCam', -'Switch-OBSInputMute', -'Switch-OBSOutput', -'Switch-OBSRecord', -'Switch-OBSRecordPause', -'Switch-OBSReplayBuffer', -'Switch-OBSStream', -'Switch-OBSVirtualCam', -'Get-OBSEffect', -'Import-OBSEffect', -'Remove-OBSEffect', -'Start-OBSEffect', -'Stop-OBSEffect' +'Set-OBSWindowSource' } From 475e0c9edd2444bc5bcd2b413deeba23d2d6a7ab Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:54:55 +0000 Subject: [PATCH 263/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- docs/obs-powershell-commands.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/obs-powershell-commands.md b/docs/obs-powershell-commands.md index 7104d20f..43aeb69d 100644 --- a/docs/obs-powershell-commands.md +++ b/docs/obs-powershell-commands.md @@ -1,8 +1,8 @@ obs-powershell-commands ----------------------- -obs-powershell exports 839 commands -(334 functions and 505 aliases) +obs-powershell exports 840 commands +(335 functions and 505 aliases) A good number of these commands directly correspond to an obs-websocket message. For a complete list, see [obs-powershell-websocket-commands](obs-powershell-websocket-commands.md). @@ -327,6 +327,7 @@ Functions |[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | |[Set-OBSWindowSource](Set-OBSWindowSource.md) | |[Show-OBS](Show-OBS.md) | +|[Start-OBS](Start-OBS.md) | |[Start-OBSEffect](Start-OBSEffect.md) | |[Start-OBSOutput](Start-OBSOutput.md) | |[Start-OBSRecord](Start-OBSRecord.md) | @@ -670,6 +671,7 @@ Aliases |[Set-OBSWaveformSource](Set-OBSWaveformSource.md) | |[Set-OBSWindowSource](Set-OBSWindowSource.md) | |[Show-OBS](Show-OBS.md) | +|[Start-OBS](Start-OBS.md) | |[Start-OBSEffect](Start-OBSEffect.md) | |[Start-OBSOutput](Start-OBSOutput.md) | |[Start-OBSRecord](Start-OBSRecord.md) | From 1bf64df3dc0bd84d7f7153ea6509855c4d6de520 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:54:55 +0000 Subject: [PATCH 264/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- docs/CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c6f75620..54f80ad4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,32 @@ > Like It? [Star It](https://github.com/StartAutomating/obs-powershell) > Love It? [Support It](https://github.com/sponsors/StartAutomating) +## obs-powershell 0.2.1: + +* New General Purpose Commands + * Start-OBS (#220) + * Stop-OBS (#226) +* New Shader Commands: + * Get-OBS3dPanelShader + * Get-OBSAudioShader + * Get-OBSCubeRotatingShader + * Get-OBSDisplacementMapAdvancedInvertShader + * Get-OBSDisplacementMapAdvancedShader + * Get-OBSDisplacementMapInvertShader + * Get-OBSDisplacementMapShader + * Get-OBSGlitchPeriodicShader + * Get-OBSHardBlinkShader + * Get-OBSMotionBlurShader + * Get-OBSNoiseShader + * Get-OBSNormalMapShader + * Get-OBSPerspectiveShader + * Get-OBSQuadrilateralCropShader + * Get-OBSRepeatGridCenterCropShader + * Get-OBSWalkingDeadPixelFixerShader + * Get-OBSZoomBlurTransitionShader + +--- + ## obs-powershell 0.2.0.1: * Fixing `Watch-OBS` (Fixes #216) From 1b57d82b54ecae49f0cd8d5f2879af2683da75f9 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:55:06 +0000 Subject: [PATCH 265/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- obs-powershell-Help.xml | 670 ++++++++++++++++++++-------------------- 1 file changed, 335 insertions(+), 335 deletions(-) diff --git a/obs-powershell-Help.xml b/obs-powershell-Help.xml index 792e9b33..75916cfb 100644 --- a/obs-powershell-Help.xml +++ b/obs-powershell-Help.xml @@ -8,7 +8,7 @@ Add-OBSInput : CreateInput - 0.2.0.1 + 0.2.1 Creates a new input, adding it as a scene item to the specified scene. @@ -247,7 +247,7 @@ This can increase performance, and also silently ignore critical errors Add-OBSProfile : CreateProfile - 0.2.0.1 + 0.2.1 Creates a new profile, switching to it in the process @@ -356,7 +356,7 @@ This can increase performance, and also silently ignore critical errors Add-OBSScene : CreateScene - 0.2.0.1 + 0.2.1 Creates a new scene in OBS. @@ -465,7 +465,7 @@ This can increase performance, and also silently ignore critical errors Add-OBSSceneCollection : CreateSceneCollection - 0.2.0.1 + 0.2.1 Creates a new scene collection, switching to it in the process. @@ -575,7 +575,7 @@ This can increase performance, and also silently ignore critical errors Add-OBSSceneItem : CreateSceneItem - 0.2.0.1 + 0.2.1 Creates a new scene item using a source. @@ -789,7 +789,7 @@ This can increase performance, and also silently ignore critical errors Add-OBSSourceFilter : CreateSourceFilter - 0.2.0.1 + 0.2.1 Creates a new filter, adding it to the specified source. @@ -1002,7 +1002,7 @@ This can increase performance, and also silently ignore critical errors Clears a Scene in OBS - 0.2.0.1 + 0.2.1 Clears a Scene in OBS. @@ -1062,7 +1062,7 @@ This can increase performance, and also silently ignore critical errors Connects to Open Broadcast Studio - 0.2.0.1 + 0.2.1 Connects to the obs-websocket. @@ -1159,7 +1159,7 @@ You can see the websocket password in Tools -> obs-websocket settings -> s Copy-OBSSceneItem : DuplicateSceneItem - 0.2.0.1 + 0.2.1 Duplicates a scene item, copying all transform and crop info. @@ -1373,7 +1373,7 @@ This can increase performance, and also silently ignore critical errors Disconnects OBS - 0.2.0.1 + 0.2.1 Disconnects Websockets from OBS. @@ -1413,7 +1413,7 @@ This can increase performance, and also silently ignore critical errors Gets OBS - 0.2.0.1 + 0.2.1 Outputs OBS connection information and state. @@ -1445,7 +1445,7 @@ This can increase performance, and also silently ignore critical errors Get-OBS3dPanelShader [[-Credits] <string>] [[-Scale] <float>] [[-TiltXDeg] <float>] [[-TiltYDeg] <float>] [[-TiltZDeg] <float>] [[-PosX] <float>] [[-PosY] <float>] [[-Thickness] <float>] [[-RadiusFb] <float>] [[-Brightness] <float>] [[-LightPosition] <int>] [[-Wiggle] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-WiggleRot] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -2043,7 +2043,7 @@ This can increase performance, and also silently ignore critical errors Get-OBS3dSwapTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-Reflection] <float>] [[-Perspective] <float>] [[-Depth] <float>] [[-BackgroundColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -2501,7 +2501,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAddShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -2763,7 +2763,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAlphaBorderShader [[-BorderColor] <string>] [[-BorderThickness] <int>] [[-AlphaCutOff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -3081,7 +3081,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAlphaGamingBentCameraShader [[-LeftSideWidth] <float>] [[-LeftSideSize] <float>] [[-LeftSideShadow] <float>] [[-LeftFlipWidth] <float>] [[-LeftFlipShadow] <float>] [[-RightSideWidth] <float>] [[-RightSideSize] <float>] [[-RightSideShadow] <float>] [[-RightFlipWidth] <float>] [[-RightFlipShadow] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -3595,7 +3595,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAnimatedPathShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-SpeedPercent] <int>] [[-PathMap] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Reverse] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -4109,7 +4109,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAnimatedTextureShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-Notes] <string>] [[-AnimationImage] <string>] [[-ColorizationImage] <string>] [[-PolarAngle] <float>] [[-PolarHeight] <float>] [[-SpeedHorizontalPercent] <float>] [[-SpeedVerticalPercent] <float>] [[-TintSpeedHorizontalPercent] <float>] [[-TintSpeedVerticalPercent] <float>] [[-Alpha] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Reverse] [-Bounce] [-CenterAnimation] [-PolarAnimation] [-UseAnimationImageColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -4987,7 +4987,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAsciiShader [[-Scale] <int>] [[-BaseColor] <string>] [[-CharacterSet] <int>] [[-Note] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Monochrome] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -5361,7 +5361,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAspectRatioShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-BorderColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -5875,7 +5875,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSAudioShader [[-AudioPeak] <float>] [[-AudioMagnitude] <float>] [[-Intensity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -6193,7 +6193,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBackgroundRemovalShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-Notes] <string>] [[-Target] <string>] [[-Color] <string>] [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-Convert709to601] [-Convert601to709] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -6847,7 +6847,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBlendOpacityShader [[-RotationOffset] <float>] [[-OpacityStartPercent] <float>] [[-OpacityEndPercent] <float>] [[-Spread] <float>] [[-Speed] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Vertical] [-Rotational] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -7333,7 +7333,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBlinkShader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -7595,7 +7595,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBloomShader [[-AngleSteps] <int>] [[-RadiusSteps] <int>] [[-AmpFactor] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -7941,7 +7941,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBorderShader [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -8203,7 +8203,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBoxBlurShader [[-Strength] <int>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -8577,7 +8577,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBulgePinchShader [[-Radius] <float>] [[-Magnitude] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animate] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -8979,7 +8979,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSBurnShader [[-BurnGradient] <string>] [[-Speed] <float>] [[-GradientAdjust] <float>] [[-DissolveValue] <float>] [[-SmokeHorizonalSpeed] <float>] [[-SmokeVerticalSpeed] <float>] [[-Iterations] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animated] [-ApplyToChannel] [-ApplySmoke] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -9521,7 +9521,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCartoonShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-Notes] <string>] [[-HueSteps] <int>] [[-ValueSteps] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -10091,7 +10091,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCellShadedShader [[-AngleSteps] <int>] [[-RadiusSteps] <int>] [[-AmpFactor] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -10437,7 +10437,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSChromaticAberrationShader [[-Power] <float>] [[-Gamma] <float>] [[-NumIter] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-DistortRadial] [-DistortBarrel] [-OffsetSpectrumYcgco] [-OffsetSpectrumYuv] [-UseRandom] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -10895,7 +10895,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSChromaUVDistortionShader [[-Distortion] <float>] [[-Amplitude] <float>] [[-Chroma] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -11213,7 +11213,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCircleMaskFilterShader [[-Radius] <float>] [[-CircleOffsetX] <int>] [[-CircleOffsetY] <int>] [[-SourceOffsetX] <int>] [[-SourceOffsetY] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Antialiasing] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -11615,7 +11615,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSClockAnalogShader [[-CurrentTimeMs] <int>] [[-CurrentTimeSec] <int>] [[-CurrentTimeMin] <int>] [[-CurrentTimeHour] <int>] [[-HourHandleColor] <float[]>] [[-MinuteHandleColor] <float[]>] [[-SecondHandleColor] <float[]>] [[-OutlineColor] <float[]>] [[-TopLineColor] <float[]>] [[-BackgroundColor] <float[]>] [[-TimeOffsetHours] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -12157,7 +12157,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSClockDigitalLedShader [[-CurrentTimeSec] <int>] [[-CurrentTimeMin] <int>] [[-CurrentTimeHour] <int>] [[-TimeMode] <int>] [[-LedColor] <string>] [[-OffsetHours] <int>] [[-OffsetSeconds] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowMatrix] [-ShowOff] [-Ampm] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -12671,7 +12671,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSClockDigitalNixieShader [[-CurrentTimeMs] <int>] [[-CurrentTimeSec] <int>] [[-CurrentTimeMin] <int>] [[-CurrentTimeHour] <int>] [[-TimeMode] <int>] [[-OffsetHours] <int>] [[-OffsetSeconds] <int>] [[-Corecolor] <float[]>] [[-Halocolor] <float[]>] [[-Flarecolor] <float[]>] [[-Anodecolor] <float[]>] [[-Anodehighlightscolor] <float[]>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -13241,7 +13241,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSColorDepthShader [[-ColorDepth] <float>] [[-PixelSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -13531,7 +13531,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSColorGradeFilterShader [[-Notes] <string>] [[-Lut] <string>] [[-LutAmountPercent] <int>] [[-LutScalePercent] <int>] [[-LutOffsetPercent] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -13905,7 +13905,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCornerPinShader [[-TopLeftX] <float>] [[-TopLeftY] <float>] [[-TopRightX] <float>] [[-TopRightY] <float>] [[-BottomLeftX] <float>] [[-BottomLeftY] <float>] [[-BottomRightX] <float>] [[-BottomRightY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-AntialiasEdges] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -14391,7 +14391,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCrtCurvatureShader [[-Strength] <float>] [[-Border] <string>] [[-Feathering] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -14709,7 +14709,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCubeRotatingShader [[-Images] <int>] [[-Speed] <float>] [[-Shadow] <float>] [[-OtherImage1] <string>] [[-OtherImage2] <string>] [[-OtherImage3] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -15111,7 +15111,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCurrentPreviewScene : GetCurrentPreviewScene - 0.2.0.1 + 0.2.1 Gets the current preview scene. @@ -15207,7 +15207,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCurrentProgramScene : GetCurrentProgramScene - 0.2.0.1 + 0.2.1 Gets the current program scene. @@ -15302,7 +15302,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCurrentSceneTransition : GetCurrentSceneTransition - 0.2.0.1 + 0.2.1 Gets information about the current scene transition. @@ -15396,7 +15396,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCurrentSceneTransitionCursor : GetCurrentSceneTransitionCursor - 0.2.0.1 + 0.2.1 Gets the cursor position of the current scene transition. @@ -15491,7 +15491,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCurveShader [[-Strength] <float>] [[-Scale] <float>] [[-CurveColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -15809,7 +15809,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCutRectPerCornerShader [[-CornerTl] <int>] [[-CornerTr] <int>] [[-CornerBr] <int>] [[-CornerBl] <int>] [[-BorderThickness] <int>] [[-BorderColor] <string>] [[-BorderAlphaStart] <float>] [[-BorderAlphaEnd] <float>] [[-AlphaCutOff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -16295,7 +16295,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSCylinderShader [[-CylinderFactor] <float>] [[-BackgroundCut] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -16585,7 +16585,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDarkenShader [[-OpacityPercentage] <float>] [[-FillPercentage] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -16903,7 +16903,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDeadPixelFixerShader [[-DeadPixelX] <int>] [[-DeadPixelY] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -17193,7 +17193,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDensitySatHueShader [[-Notes] <string>] [[-DensityR] <float>] [[-SaturationR] <float>] [[-HueShiftR] <float>] [[-DensityY] <float>] [[-SaturationY] <float>] [[-HueShiftY] <float>] [[-DensityG] <float>] [[-SaturationG] <float>] [[-HueShiftG] <float>] [[-DensityC] <float>] [[-SaturationC] <float>] [[-HueShiftC] <float>] [[-DensityB] <float>] [[-SaturationB] <float>] [[-HueShiftB] <float>] [[-DensityM] <float>] [[-SaturationM] <float>] [[-HueShiftM] <float>] [[-GlobalDensity] <float>] [[-GlobalSaturation] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -18015,7 +18015,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDiffuseTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-NumSamples] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -18389,7 +18389,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDigitalRainShader [[-Font] <string>] [[-Noise] <string>] [[-BaseColor] <string>] [[-RainSpeed] <float>] [[-CharSpeed] <float>] [[-GlowContrast] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -18791,7 +18791,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDisplacementMapAdvancedInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -19585,7 +19585,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDisplacementMapAdvancedShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-DisplacementCurve] <int>] [[-BlurInfo] <string>] [[-BlurSize] <float>] [[-BlurQuality] <float>] [[-BlurDirections] <float>] [[-BlurAngle] <float>] [[-ChromaticAberrationInfo] <string>] [[-ChromaticAberration] <float>] [[-ColorizeInfo] <string>] [[-ColorizeColor] <string>] [[-FlagsInfo] <string>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-BlueAffectsStrength] [-BlueAffectsColorize] [-BlueAffectsBlur] [-AlphaAffectsStrength] [-ApplyAlpha] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -20379,7 +20379,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDisplacementMapInvertShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-BackgroundLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -20725,7 +20725,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDisplacementMapShader [[-DisplacementInfo] <string>] [[-DisplacementX] <float>] [[-DisplacementY] <float>] [[-MaskLayer] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -21071,7 +21071,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDivideRotateShader [[-IChannel0] <string>] [[-SpeedPercentage] <int>] [[-AlphaPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -21445,7 +21445,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDoodleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-DoodleScalePercent] <float>] [[-SnapPercent] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -21987,7 +21987,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDrawingsShader [[-AngleNum] <int>] [[-SampNum] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -22277,7 +22277,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDropShadowShader [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-Notes] <string>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -22679,7 +22679,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDrunkShader [[-ColorMatrix] <float[][]>] [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeedPercent] <int>] [[-GlowColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -23221,7 +23221,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSDynamicMaskShader [[-InputSource] <string>] [[-RedBaseValue] <float>] [[-RedRedInputValue] <float>] [[-RedGreenInputValue] <float>] [[-RedBlueInputValue] <float>] [[-RedAlphaInputValue] <float>] [[-RedMultiplier] <float>] [[-GreenBaseValue] <float>] [[-GreenRedInputValue] <float>] [[-GreenGreenInputValue] <float>] [[-GreenBlueInputValue] <float>] [[-GreenAlphaInputValue] <float>] [[-GreenMultiplier] <float>] [[-BlueBaseValue] <float>] [[-BlueRedInputValue] <float>] [[-BlueGreenInputValue] <float>] [[-BlueBlueInputValue] <float>] [[-BlueAlphaInputValue] <float>] [[-BlueMultiplier] <float>] [[-AlphaBaseValue] <float>] [[-AlphaRedInputValue] <float>] [[-AlphaGreenInputValue] <float>] [[-AlphaBlueInputValue] <float>] [[-AlphaAlphaInputValue] <float>] [[-AlphaMultiplier] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -24155,7 +24155,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSEdgeDetectionShader [[-Sensitivity] <float>] [[-EdgeColor] <string>] [[-NonEdgeColor] <string>] [[-AlphaLevel] <float>] [[-RandF] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertEdge] [-EdgeMultiply] [-NonEdgeMultiply] [-AlphaChannel] [-AlphaInvert] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -24697,7 +24697,7 @@ This can increase performance, and also silently ignore critical errors Gets OBS Effects - 0.2.0.1 + 0.2.1 Gets effects currently loaded into OBS-PowerShell. @@ -24762,7 +24762,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSEmbersShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-AnimationSpeed] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-MovementSpeedPercent] <int>] [[-LayersCount] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -25612,7 +25612,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSEmbossColorShader [[-AngleSteps] <int>] [[-RadiusSteps] <int>] [[-AmpFactor] <float>] [[-UpDownPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -26014,7 +26014,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSEmbossShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-UseColor] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -26304,7 +26304,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSExeldroBentCameraShader [[-LeftSideWidth] <float>] [[-LeftSideSize] <float>] [[-LeftSideShadow] <float>] [[-LeftFlipWidth] <float>] [[-LeftFlipShadow] <float>] [[-RightSideWidth] <float>] [[-RightSideSize] <float>] [[-RightSideShadow] <float>] [[-RightFlipWidth] <float>] [[-RightFlipShadow] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -26818,7 +26818,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFadeTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -27164,7 +27164,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFillColorGradientShader [[-Fill] <float>] [[-GradientWidth] <float>] [[-GradientOffset] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -27538,7 +27538,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFillColorLinearShader [[-Fill] <float>] [[-FillDirection] <int>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -27856,7 +27856,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFillColorRadialDegreesShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -28258,7 +28258,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFillColorRadialPercentageShader [[-FillDirection] <int>] [[-Fill] <float>] [[-StartAngle] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-FillColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -28660,7 +28660,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFilterTemplateShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -29258,7 +29258,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFire3Shader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-MovementDirectionHorizontal] <float>] [[-MovementDirectionVertical] <float>] [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-FlameSize] <float>] [[-SparkGridHeight] <float>] [[-FlameModifier] <float>] [[-FlameTongueSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-FullWidth] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -30276,7 +30276,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFireShader [[-AlphaPercentage] <int>] [[-Speed] <int>] [[-FlameSize] <int>] [[-FireType] <int>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Invert] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -30846,7 +30846,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFireworks2Shader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -31108,7 +31108,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFireworksShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowFlash] [-ShowStars] [-UseTransparancy] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -31426,7 +31426,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFisheyeShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-Power] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -31744,7 +31744,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFisheyeXyShader [[-CenterXPercent] <float>] [[-CenterYPercent] <float>] [[-PowerX] <float>] [[-PowerY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -32090,7 +32090,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFlipShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -32380,7 +32380,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSFrostedGlassShader [[-AlphaPercent] <float>] [[-Amount] <float>] [[-Scale] <float>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animate] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -32838,7 +32838,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGammaCorrectionShader [[-Red] <float>] [[-Green] <float>] [[-Blue] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -33184,7 +33184,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGaussianBlurAdvancedShader [[-Directions] <float>] [[-Quality] <float>] [[-Size] <float>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -33614,7 +33614,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGaussianBlurShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ImageSize] <float[]>] [[-ImageTexel] <float[]>] [[-URadius] <int>] [[-UDiameter] <int>] [[-UTexelDelta] <float[]>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-Kernel] <string>] [[-KernelTexel] <float[]>] [[-PixelSize] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -34240,7 +34240,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGaussianBlurSimpleShader [[-Strength] <int>] [[-MaskLeft] <float>] [[-MaskRight] <float>] [[-MaskTop] <float>] [[-MaskBottom] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -34614,7 +34614,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGaussianExampleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-InitialImage] <string>] [[-BeforeImage] <string>] [[-AfterImage] <string>] [[-TextColor] <string>] [[-MaxDistance] <float>] [[-Exp] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -35212,7 +35212,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGaussianSimpleShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Samples] <int>] [[-LOD] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -35838,7 +35838,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGbCameraShader [[-PixelSize] <float>] [[-DitherFactor] <float>] [[-Brightness] <float>] [[-Contrast] <float>] [[-Gamma] <float>] [[-Color1] <string>] [[-Color2] <string>] [[-Color3] <string>] [[-Color4] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-AlternativeBayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -36352,7 +36352,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGlassShader [[-AlphaPercent] <float>] [[-OffsetAmount] <float>] [[-XSize] <int>] [[-YSize] <int>] [[-ReflectionOffset] <int>] [[-BorderOffset] <float>] [[-BorderColor] <string>] [[-GlassColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HorizontalBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -36866,7 +36866,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGlitchAnalogShader [[-ScanLineJitterDisplacement] <float>] [[-ScanLineJitterThresholdPercent] <int>] [[-VerticalJumpAmount] <float>] [[-VerticalSpeed] <float>] [[-HorizontalShake] <float>] [[-ColorDriftAmount] <float>] [[-ColorDriftSpeed] <float>] [[-PulseSpeedPercent] <int>] [[-AlphaPercent] <int>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-RotateColors] [-ApplyToAlphaLayer] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -37520,7 +37520,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGlitchPeriodicShader [[-PERI] <float>] [[-DURA] <float>] [[-AMPL] <float>] [[-SCRA] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -37866,7 +37866,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGlitchShader [[-AMT] <float>] [[-SPEED] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -38156,7 +38156,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGlowShader [[-GlowPercent] <int>] [[-Blur] <int>] [[-MinBrightness] <int>] [[-MaxBrightness] <int>] [[-PulseSpeed] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -38586,7 +38586,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGradientShader [[-StartColor] <string>] [[-StartStep] <float>] [[-MiddleColor] <string>] [[-MiddleStep] <float>] [[-EndColor] <string>] [[-EndStep] <float>] [[-AlphaPercent] <int>] [[-PulseSpeed] <int>] [[-ColorToReplace] <string>] [[-GradientCenterWidthPercentage] <int>] [[-GradientCenterHeightPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-RotateColors] [-ApplyToAlphaLayer] [-ApplyToSpecificColor] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -39324,7 +39324,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGroup : GetGroupList - 0.2.0.1 + 0.2.1 Gets an array of all groups in OBS. @@ -39419,7 +39419,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSGroupSceneItem : GetGroupSceneItemList - 0.2.0.1 + 0.2.1 Basically GetSceneItemList, but for groups. @@ -39556,7 +39556,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHalftoneShader [[-Threshold] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -39818,7 +39818,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHardBlinkShader [[-Timeon] <float>] [[-Timeoff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -40108,7 +40108,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHeatWaveSimpleShader [[-Rate] <float>] [[-Strength] <float>] [[-Distortion] <float>] [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -40454,7 +40454,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHexagonShader [[-HexColor] <string>] [[-AlphaPercent] <int>] [[-Quantity] <float>] [[-BorderWidth] <int>] [[-SpeedPercent] <int>] [[-DistortX] <float>] [[-DistortY] <float>] [[-OffsetX] <float>] [[-OffsetY] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Blend] [-Equilateral] [-ZoomAnimate] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -41080,7 +41080,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHotkey : GetHotkeyList - 0.2.0.1 + 0.2.1 Gets an array of all hotkey names in OBS. @@ -41175,7 +41175,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHslHsvSaturationShader [[-HslSaturationFactor] <float>] [[-HslGamma] <float>] [[-HsvSaturationFactor] <float>] [[-HsvGamma] <float>] [[-AdjustmentOrder] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -41549,7 +41549,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSHueRotatonShader [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-HueOverride] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -41839,7 +41839,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInput : GetInputList - 0.2.0.1 + 0.2.1 Gets an array of all inputs in OBS. @@ -41948,7 +41948,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputAudioBalance : GetInputAudioBalance - 0.2.0.1 + 0.2.1 Gets the audio balance of an input. @@ -42083,7 +42083,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputAudioMonitorType : GetInputAudioMonitorType - 0.2.0.1 + 0.2.1 Gets the audio monitor type of an input. @@ -42222,7 +42222,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputAudioSyncOffset : GetInputAudioSyncOffset - 0.2.0.1 + 0.2.1 Gets the audio sync offset of an input. @@ -42358,7 +42358,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputAudioTracks : GetInputAudioTracks - 0.2.0.1 + 0.2.1 Gets the enable state of all audio tracks of an input. @@ -42493,7 +42493,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputDefaultSettings : GetInputDefaultSettings - 0.2.0.1 + 0.2.1 Gets the default settings for an input kind. @@ -42602,7 +42602,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputKind : GetInputKindList - 0.2.0.1 + 0.2.1 Gets an array of all available input kinds in OBS. @@ -42711,7 +42711,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputMute : GetInputMute - 0.2.0.1 + 0.2.1 Gets the audio mute state of an input. @@ -42846,7 +42846,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputPropertiesListPropertyItems : GetInputPropertiesListPropertyItems - 0.2.0.1 + 0.2.1 Gets the items of a list property from an input's properties. @@ -43008,7 +43008,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputSettings : GetInputSettings - 0.2.0.1 + 0.2.1 Gets the settings of an input. @@ -43144,7 +43144,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInputVolume : GetInputVolume - 0.2.0.1 + 0.2.1 Gets the current volume setting of an input. @@ -43279,7 +43279,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSIntensityScopeShader [[-Gain] <float>] [[-Blend] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -43569,7 +43569,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSInvertLumaShader [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertColor] [-InvertLuma] [-GammaCorrection] [-TestRamp] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -43915,7 +43915,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSLastReplayBufferReplay : GetLastReplayBufferReplay - 0.2.0.1 + 0.2.1 Gets the filename of the last replay buffer save file. @@ -44009,7 +44009,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSLuminance2Shader [[-Color] <string>] [[-LumaMax] <float>] [[-LumaMin] <float>] [[-LumaMaxSmooth] <float>] [[-LumaMinSmooth] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -44467,7 +44467,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSLuminanceAlphaShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ColorMatrix] <float[][]>] [[-Color] <string>] [[-MulVal] <float>] [[-AddVal] <float>] [[-Level] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -45093,7 +45093,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSLuminanceShader [[-Color] <string>] [[-Level] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertImageColor] [-InvertAlphaChannel] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -45467,7 +45467,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSMatrixShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvSize] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-RandInstanceF] <float>] [[-RandActivationF] <float>] [[-Loops] <int>] [[-LocalTime] <float>] [[-Mouse] <float[]>] [[-LumaMin] <float>] [[-LumaMinSmooth] <float>] [[-Ratio] <float>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-InvertDirection] [-ApplyToAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -46233,7 +46233,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSMediaInputStatus : GetMediaInputStatus - 0.2.0.1 + 0.2.1 Gets the status of a media input. @@ -46377,7 +46377,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSMonitor : GetMonitorList - 0.2.0.1 + 0.2.1 Gets a list of connected monitors and information about them. @@ -46471,7 +46471,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSMotionBlurShader [[-PreviousOutput] <string>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -46761,7 +46761,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSMultiplyShader [[-OtherImage] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -47023,7 +47023,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSNightSkyShader [[-Speed] <float>] [[-CenterWidthPercentage] <int>] [[-CenterHeightPercentage] <int>] [[-AlphaPercentage] <float>] [[-NumberStars] <int>] [[-SKYCOLOR] <string>] [[-STARCOLOR] <string>] [[-LIGHTSKY] <string>] [[-SKYLIGHTNESS] <float>] [[-MOONCOLOR] <string>] [[-MoonSize] <float>] [[-MoonBumpSize] <float>] [[-MoonPositionX] <float>] [[-MoonPositionY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IncludeClouds] [-IncludeMoon] [-ApplyToImage] [-ReplaceImageColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -47761,7 +47761,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSNoiseShader [[-Speed] <float>] [[-Scale] <float>] [[-NoiseLevel] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Monochromatic] [-UseRand] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -48135,7 +48135,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSNormalMapShader [[-Strength] <float>] [[-Type] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-OffsetHeight] [-InvertR] [-InvertG] [-InvertH] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -48537,7 +48537,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSOpacityShader [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -48799,7 +48799,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSOutput : GetOutputList - 0.2.0.1 + 0.2.1 Gets the list of available outputs. @@ -48893,7 +48893,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSOutputSettings : GetOutputSettings - 0.2.0.1 + 0.2.1 Gets the settings of an output. @@ -49002,7 +49002,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSOutputStatus : GetOutputStatus - 0.2.0.1 + 0.2.1 Gets the status of an output. @@ -49111,7 +49111,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPagePeelShader [[-Speed] <float>] [[-Position] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -49401,7 +49401,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPagePeelTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-PageColor] <string>] [[-PageTransparency] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -49803,7 +49803,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPerlinNoiseShader [[-Speed] <float>] [[-SpeedHorizonal] <float>] [[-SpeedVertical] <float>] [[-Iterations] <int>] [[-WhiteNoise] <float>] [[-BlackNoise] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animated] [-ApplyToChannel] [-Inverted] [-Multiply] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -50345,7 +50345,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPersistentData : GetPersistentData - 0.2.0.1 + 0.2.1 Gets the value of a "slot" from the selected persistent data realm. @@ -50480,7 +50480,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPerspectiveShader [[-AngleX] <float>] [[-AngleY] <float>] [[-AngleZ] <float>] [[-Perspective] <float>] [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -50882,7 +50882,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPieChartShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-StartAngle] <float>] [[-Total] <int>] [[-Part1] <int>] [[-Color1] <string>] [[-Part2] <int>] [[-Color2] <string>] [[-Part3] <int>] [[-Color3] <string>] [[-Part4] <int>] [[-Color4] <string>] [[-Part5] <int>] [[-Color5] <string>] [[-Part6] <int>] [[-Color6] <string>] [[-Part7] <int>] [[-Color7] <string>] [[-Part8] <int>] [[-Color8] <string>] [[-Part9] <int>] [[-Color9] <string>] [[-Part10] <int>] [[-Color10] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -51788,7 +51788,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPixelationShader [[-TargetWidth] <float>] [[-TargetHeight] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -52106,7 +52106,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPixelationTransitionShader [[-TransitionTime] <float>] [[-Power] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -52480,7 +52480,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPolarShader [[-CenterX] <float>] [[-CenterY] <float>] [[-PointY] <float>] [[-Rotate] <float>] [[-Repeat] <float>] [[-Scale] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Flip] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -52910,7 +52910,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSProfile : GetProfileList - 0.2.0.1 + 0.2.1 Gets an array of all profiles @@ -53004,7 +53004,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSProfileParameter : GetProfileParameter - 0.2.0.1 + 0.2.1 Gets a parameter from the current profile's configuration. @@ -53139,7 +53139,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSPulseShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-Speed] <float>] [[-MinGrowthPixels] <float>] [[-MaxGrowthPixels] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -53681,7 +53681,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSQuadrilateralCropShader [[-TopLeftX] <float>] [[-TopLeftY] <float>] [[-TopRightX] <float>] [[-TopRightY] <float>] [[-BottomLeftX] <float>] [[-BottomLeftY] <float>] [[-BottomRightX] <float>] [[-BottomRightY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -54139,7 +54139,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRainbowShader [[-Saturation] <float>] [[-Luminosity] <float>] [[-Spread] <float>] [[-Speed] <float>] [[-AlphaPercentage] <float>] [[-RotationOffset] <float>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Vertical] [-Rotational] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -54737,7 +54737,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRainWindowShader [[-Size] <float>] [[-BlurSize] <float>] [[-TrailStrength] <float>] [[-TrailColor] <float>] [[-Speed] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-DebugShader] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -55139,7 +55139,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRecordDirectory : GetRecordDirectory - 0.2.0.1 + 0.2.1 Gets the current directory that the record output is set to. @@ -55233,7 +55233,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRecordStatus : GetRecordStatus - 0.2.0.1 + 0.2.1 Gets the status of the record output. @@ -55327,7 +55327,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRectangularDropShadowShader [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -55673,7 +55673,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSReflectShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Horizontal] [-Vertical] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -56019,7 +56019,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRemovePartialPixelsShader [[-MinimumAlphaPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -56309,7 +56309,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRepeatGridCenterCropShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-Alpha] <float>] [[-Columns] <float>] [[-Rows] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -56683,7 +56683,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRepeatShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Alpha] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -57309,7 +57309,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRepeatTextureShader [[-ViewProj] <float[][]>] [[-ColorMatrix] <float[][]>] [[-ColorRangeMin] <float[]>] [[-ColorRangeMax] <float[]>] [[-Image] <string>] [[-TexImage] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-UvSize] <float[]>] [[-RandF] <float>] [[-Blend] <float>] [[-Copies] <float>] [[-Notes] <string>] [[-AlphaPercentage] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -57991,7 +57991,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSReplayBufferStatus : GetReplayBufferStatus - 0.2.0.1 + 0.2.1 Gets the status of the replay buffer output. @@ -58085,7 +58085,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRGBAPercentShader [[-RedPercent] <float>] [[-GreenPercent] <float>] [[-BluePercent] <float>] [[-AlphaPercent] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -58431,7 +58431,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRgbColorWheelShader [[-Speed] <float>] [[-ColorDepth] <float>] [[-ColorToReplace] <string>] [[-AlphaPercentage] <float>] [[-CenterWidthPercentage] <int>] [[-CenterHeightPercentage] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -58917,7 +58917,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRgbSplitShader [[-Redx] <float>] [[-Redy] <float>] [[-Greenx] <float>] [[-Greeny] <float>] [[-Bluex] <float>] [[-Bluey] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -59319,7 +59319,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRgbvisibilityShader [[-Red] <float>] [[-Green] <float>] [[-Blue] <float>] [[-RedVisibility] <float>] [[-GreenVisibility] <float>] [[-BlueVisibility] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -59749,7 +59749,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRGSSAAShader [[-ColorSigma] <float>] [[-SpatialSigma] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -60067,7 +60067,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRippleShader [[-DistanceFactor] <float>] [[-TimeFactor] <float>] [[-PowerFactor] <float>] [[-CenterPosX] <float>] [[-CenterPosY] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -60441,7 +60441,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRotatingSourceShader [[-SpinSpeed] <float>] [[-Rotation] <float>] [[-Zoomin] <float>] [[-XCenter] <float>] [[-YCenter] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-KeepAspectratio] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -60843,7 +60843,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRotatoeShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-SpeedPercent] <int>] [[-AxisX] <float>] [[-AxisY] <float>] [[-AxisZ] <float>] [[-AngleDegrees] <float>] [[-CenterWidthPercentage] <int>] [[-CenterHeightPercentage] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-RotateTransform] [-RotatePixels] [-RotateColors] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -61609,7 +61609,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedRect2Shader [[-CornerRadius] <int>] [[-BorderThickness] <int>] [[-BorderColor] <string>] [[-BorderAlphaStart] <float>] [[-BorderAlphaEnd] <float>] [[-AlphaCutOff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-FasterScan] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -62039,7 +62039,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedRectPerCornerShader [[-CornerRadiusTl] <int>] [[-CornerRadiusTr] <int>] [[-CornerRadiusBr] <int>] [[-CornerRadiusBl] <int>] [[-BorderThickness] <int>] [[-BorderColor] <string>] [[-BorderAlphaStart] <float>] [[-BorderAlphaEnd] <float>] [[-AlphaCutOff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -62525,7 +62525,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedRectPerSideShader [[-CornerRadiusBottom] <int>] [[-CornerRadiusLeft] <int>] [[-CornerRadiusTop] <int>] [[-CornerRadiusRight] <int>] [[-BorderThickness] <int>] [[-BorderColor] <string>] [[-BorderAlphaStart] <float>] [[-BorderAlphaEnd] <float>] [[-AlphaCutOff] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -63011,7 +63011,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedRectShader [[-CornerRadius] <int>] [[-BorderThickness] <int>] [[-BorderColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -63329,7 +63329,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedStrokeGradientShader [[-CornerRadius] <int>] [[-BorderThickness] <int>] [[-MinimumAlphaPercent] <int>] [[-RotationSpeed] <int>] [[-BorderColorL] <string>] [[-BorderColorR] <string>] [[-CenterWidth] <int>] [[-CenterHeight] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -63815,7 +63815,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSRoundedStrokeShader [[-CornerRadius] <int>] [[-BorderThickness] <int>] [[-MinimumAlphaPercent] <int>] [[-BorderColor] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -64189,7 +64189,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSScanLineShader [[-Speed] <float>] [[-Angle] <float>] [[-Floor] <float>] [[-Period] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Lengthwise] [-Animate] [-Shift] [-Boost] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -64675,7 +64675,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSScene : GetSceneList - 0.2.0.1 + 0.2.1 Gets an array of all scenes in OBS. @@ -64769,7 +64769,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneCollection : GetSceneCollectionList - 0.2.0.1 + 0.2.1 Gets an array of all scene collections @@ -64863,7 +64863,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItem : GetSceneItemList - 0.2.0.1 + 0.2.1 Gets a list of all scene items in a scene. @@ -64999,7 +64999,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemBlendMode : GetSceneItemBlendMode - 0.2.0.1 + 0.2.1 Gets the blend mode of a scene item. @@ -65169,7 +65169,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemEnabled : GetSceneItemEnabled - 0.2.0.1 + 0.2.1 Gets the enable state of a scene item. @@ -65331,7 +65331,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemId : GetSceneItemId - 0.2.0.1 + 0.2.1 Searches a scene for a source, and returns its id. @@ -65519,7 +65519,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemIndex : GetSceneItemIndex - 0.2.0.1 + 0.2.1 Gets the index position of a scene item in a scene. @@ -65682,7 +65682,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemLocked : GetSceneItemLocked - 0.2.0.1 + 0.2.1 Gets the lock state of a scene item. @@ -65844,7 +65844,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemSource : GetSceneItemSource - 0.2.0.1 + 0.2.1 Gets the source associated with a scene item. @@ -66005,7 +66005,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneItemTransform : GetSceneItemTransform - 0.2.0.1 + 0.2.1 Gets the transform and crop info of a scene item. @@ -66167,7 +66167,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneSceneTransitionOverride : GetSceneSceneTransitionOverride - 0.2.0.1 + 0.2.1 Gets the scene transition overridden for a scene. @@ -66303,7 +66303,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSceneTransition : GetSceneTransitionList - 0.2.0.1 + 0.2.1 Gets an array of all scene transitions in OBS. @@ -66397,7 +66397,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSeascapeShader [[-SEAHEIGHT] <float>] [[-SEACHOPPY] <float>] [[-SEASPEED] <float>] [[-SEAFREQ] <float>] [[-SEABASE] <string>] [[-SEAWATERCOLOR] <string>] [[-CAMERASPEED] <float>] [[-CAMERATURNSPEED] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-AA] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -66883,7 +66883,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSeasickShader [[-Notes] <string>] [[-Amplitude] <float>] [[-Speed] <float>] [[-Frequency] <float>] [[-Opacity] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -67257,7 +67257,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSelectiveColorShader [[-CutoffRed] <float>] [[-CutoffGreen] <float>] [[-CutoffBlue] <float>] [[-CutoffYellow] <float>] [[-AcceptanceAmplifier] <float>] [[-Notes] <string>] [[-BackgroundType] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowRed] [-ShowGreen] [-ShowBlue] [-ShowYellow] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -67799,7 +67799,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSShakeShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-LocalTime] <float>] [[-RandomScale] <float>] [[-Speed] <float>] [[-MinGrowthPixels] <float>] [[-MaxGrowthPixels] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Worble] [-RandomizeMovement] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -68481,7 +68481,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSShineShader [[-LTex] <string>] [[-ShineColor] <string>] [[-SpeedPercent] <int>] [[-GradientPercent] <int>] [[-DelayPercent] <int>] [[-Notes] <string>] [[-StartAdjust] <float>] [[-StopAdjust] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToAlphaLayer] [-Ease] [-Hide] [-Reverse] [-OneDirection] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -69107,7 +69107,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSimpleGradientShader [[-SpeedPercentage] <int>] [[-AlphaPercentage] <int>] [[-ColorToReplace] <string>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-LensFlair] [-AnimateLensFlair] [-ApplyToAlphaLayer] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -69565,7 +69565,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSimplexNoiseShader [[-SnapPercent] <float>] [[-SpeedPercent] <float>] [[-Resolution] <float>] [[-ForeColor] <string>] [[-BackColor] <string>] [[-AlphaPercent] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Fractal] [-UseAlphaLayer] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -70051,7 +70051,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSmartDenoiseShader [[-USigma] <float>] [[-UKSigma] <float>] [[-UThreshold] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -70369,7 +70369,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceActive : GetSourceActive - 0.2.0.1 + 0.2.1 Gets the active and show state of a source. @@ -70505,7 +70505,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceFilter : GetSourceFilter - 0.2.0.1 + 0.2.1 Gets the info for a specific source filter. @@ -70666,7 +70666,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceFilterDefaultSettings : GetSourceFilterDefaultSettings - 0.2.0.1 + 0.2.1 Gets the default settings for a filter kind. @@ -70775,7 +70775,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceFilterKind : GetSourceFilterKindList - 0.2.0.1 + 0.2.1 Gets an array of all available source filter kinds. @@ -70870,7 +70870,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceFilterList : GetSourceFilterList - 0.2.0.1 + 0.2.1 Gets an array of all of a source's filters. @@ -71005,7 +71005,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSourceScreenshot : GetSourceScreenshot - 0.2.0.1 + 0.2.1 Gets a Base64-encoded screenshot of a source. @@ -71247,7 +71247,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSpecialInputs : GetSpecialInputs - 0.2.0.1 + 0.2.1 Gets the names of all special inputs. @@ -71341,7 +71341,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSpecularShineShader [[-Hint] <string>] [[-Roughness] <float>] [[-LightStrength] <float>] [[-LightPositionX] <float>] [[-LightPositionY] <float>] [[-FlattenNormal] <float>] [[-StretchNormalX] <float>] [[-StretchNormalY] <float>] [[-LightColor] <float[]>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -71827,7 +71827,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSpotlightShader [[-SpeedPercent] <float>] [[-FocusPercent] <float>] [[-SpotlightColor] <string>] [[-HorizontalOffset] <float>] [[-VerticalOffset] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -72257,7 +72257,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSStats : GetStats - 0.2.0.1 + 0.2.1 Gets statistics about OBS, obs-websocket, and the current session. @@ -72351,7 +72351,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSStreamServiceSettings : GetStreamServiceSettings - 0.2.0.1 + 0.2.1 Gets the current stream service settings (stream destination). @@ -72445,7 +72445,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSStreamStatus : GetStreamStatus - 0.2.0.1 + 0.2.1 Gets the status of the stream output. @@ -72539,7 +72539,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSStudioModeEnabled : GetStudioModeEnabled - 0.2.0.1 + 0.2.1 Gets whether studio is enabled. @@ -72633,7 +72633,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSSwirlShader [[-Radius] <float>] [[-Angle] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Animate] [-Inverse] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -73063,7 +73063,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSTetraShader [[-RedR] <float>] [[-RedG] <float>] [[-RedB] <float>] [[-YelR] <float>] [[-YelG] <float>] [[-YelB] <float>] [[-GrnR] <float>] [[-GrnG] <float>] [[-GrnB] <float>] [[-CynR] <float>] [[-CynG] <float>] [[-CynB] <float>] [[-BluR] <float>] [[-BluG] <float>] [[-BluB] <float>] [[-MagR] <float>] [[-MagG] <float>] [[-MagB] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -73801,7 +73801,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSThermalShader [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -74063,7 +74063,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSTransitionKind : GetTransitionKindList - 0.2.0.1 + 0.2.1 Gets an array of all available transition kinds. @@ -74158,7 +74158,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSTvCrtSubpixelShader [[-ChannelWidth] <int>] [[-ChannelHeight] <int>] [[-HGap] <int>] [[-VGap] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -74504,7 +74504,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSTwistShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-Power] <float>] [[-Rotation] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -74850,7 +74850,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSTwoPassDropShadowShader [[-ViewProj] <float[][]>] [[-Image] <string>] [[-ElapsedTime] <float>] [[-UvOffset] <float[]>] [[-UvScale] <float[]>] [[-UvPixelInterval] <float[]>] [[-RandF] <float>] [[-UvSize] <float[]>] [[-ShadowOffsetX] <int>] [[-ShadowOffsetY] <int>] [[-ShadowBlurSize] <int>] [[-ShadowColor] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-IsAlphaPremultiplied] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -75448,7 +75448,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVCRShader [[-VerticalShift] <float>] [[-Distort] <float>] [[-Vignet] <float>] [[-Stripe] <float>] [[-VerticalFactor] <float>] [[-VerticalHeight] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -75850,7 +75850,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVersion : GetVersion - 0.2.0.1 + 0.2.1 Gets data about the current plugin and RPC version. @@ -75944,7 +75944,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVHSShader [[-Range] <float>] [[-OffsetIntensity] <float>] [[-NoiseQuality] <float>] [[-NoiseIntensity] <float>] [[-ColorOffsetIntensity] <float>] [[-AlphaPercentage] <float>] [[-ColorToReplace] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ApplyToImage] [-ReplaceImageColor] [-ApplyToSpecificColor] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -76458,7 +76458,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVideoSettings : GetVideoSettings - 0.2.0.1 + 0.2.1 Gets the current video settings. @@ -76553,7 +76553,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVignettingShader [[-InnerRadius] <float>] [[-OuterRadius] <float>] [[-Opacity] <float>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -76899,7 +76899,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVirtualCamStatus : GetVirtualCamStatus - 0.2.0.1 + 0.2.1 Gets the status of the virtualcam output. @@ -76993,7 +76993,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSVoronoiPixelationShader [[-PixH] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Alternative] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -77283,7 +77283,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSWalkingDeadPixelFixerShader [[-ScanWidth] <int>] [[-ScanHeight] <int>] [[-ScanOffsetX] <int>] [[-ScanOffsetY] <int>] [[-ContrastThreshold] <float>] [[-MinClusterSize] <int>] [[-MaxClusterSize] <int>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ShowBorder] [-ShowGreen] [-Bypass] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -77797,7 +77797,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSZigZagShader [[-Radius] <float>] [[-Angle] <float>] [[-Period] <float>] [[-Amplitude] <float>] [[-CenterX] <float>] [[-CenterY] <float>] [[-Phase] <float>] [[-Animate] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -78283,7 +78283,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSZoomBlurShader [[-Samples] <int>] [[-Magnitude] <float>] [[-SpeedPercent] <int>] [[-Notes] <string>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Ease] [-Glitch] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -78685,7 +78685,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSZoomBlurTransitionShader [[-ImageA] <string>] [[-ImageB] <string>] [[-TransitionTime] <float>] [[-Strength] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-ConvertLinear] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -79059,7 +79059,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSZoomShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-Power] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -79377,7 +79377,7 @@ This can increase performance, and also silently ignore critical errors Get-OBSZoomXYShader [[-CenterXPercent] <int>] [[-CenterYPercent] <int>] [[-XPower] <float>] [[-YPower] <float>] [[-SourceName] <string>] [[-FilterName] <string>] [[-ShaderText] <string>] [-Force] [-PassThru] [-NoResponse] [-UseShaderTime] [<CommonParameters>] - 0.2.0.1 + 0.2.1 @@ -79723,7 +79723,7 @@ This can increase performance, and also silently ignore critical errors Hide OBS - 0.2.0.1 + 0.2.1 Hides items in OBS @@ -79814,7 +79814,7 @@ This can increase performance, and also silently ignore critical errors Imports Effects - 0.2.0.1 + 0.2.1 Imports obs-powershell effects @@ -79881,7 +79881,7 @@ This can be a string, file, directory, command, or module. Open-OBSInputFiltersDialog : OpenInputFiltersDialog - 0.2.0.1 + 0.2.1 Opens the filters dialog of an input. @@ -80016,7 +80016,7 @@ This can increase performance, and also silently ignore critical errors Open-OBSInputInteractDialog : OpenInputInteractDialog - 0.2.0.1 + 0.2.1 Opens the interact dialog of an input. @@ -80151,7 +80151,7 @@ This can increase performance, and also silently ignore critical errors Open-OBSInputPropertiesDialog : OpenInputPropertiesDialog - 0.2.0.1 + 0.2.1 Opens the properties dialog of an input. @@ -80286,7 +80286,7 @@ This can increase performance, and also silently ignore critical errors Open-OBSSourceProjector : OpenSourceProjector - 0.2.0.1 + 0.2.1 Opens a projector for a source. @@ -80474,7 +80474,7 @@ This can increase performance, and also silently ignore critical errors Open-OBSVideoMixProjector : OpenVideoMixProjector - 0.2.0.1 + 0.2.1 Opens a projector for a specific output video mix. @@ -80640,7 +80640,7 @@ This can increase performance, and also silently ignore critical errors Receives data from OBS - 0.2.0.1 + 0.2.1 Receives responses from the OBS WebSocket @@ -80809,7 +80809,7 @@ You can see the websocket password in Tools -> obs-websocket settings -> s Remove OBS - 0.2.0.1 + 0.2.1 Removes items from OBS @@ -80888,7 +80888,7 @@ You can see the websocket password in Tools -> obs-websocket settings -> s Removes OBS Effects - 0.2.0.1 + 0.2.1 Removes effects currently loaded into OBS-PowerShell. @@ -80949,7 +80949,7 @@ You can see the websocket password in Tools -> obs-websocket settings -> s Remove-OBSInput : RemoveInput - 0.2.0.1 + 0.2.1 Removes an existing input. @@ -81085,7 +81085,7 @@ This can increase performance, and also silently ignore critical errors Remove-OBSProfile : RemoveProfile - 0.2.0.1 + 0.2.1 Removes a profile. If the current profile is chosen, it will change to a different profile first. @@ -81194,7 +81194,7 @@ This can increase performance, and also silently ignore critical errors Remove-OBSScene : RemoveScene - 0.2.0.1 + 0.2.1 Removes a scene from OBS. @@ -81329,7 +81329,7 @@ This can increase performance, and also silently ignore critical errors Remove-OBSSceneItem : RemoveSceneItem - 0.2.0.1 + 0.2.1 Removes a scene item from a scene. @@ -81491,7 +81491,7 @@ This can increase performance, and also silently ignore critical errors Remove-OBSSourceFilter : RemoveSourceFilter - 0.2.0.1 + 0.2.1 Removes a filter from a source. @@ -81652,7 +81652,7 @@ This can increase performance, and also silently ignore critical errors Resume-OBSRecord : ResumeRecord - 0.2.0.1 + 0.2.1 Resumes the record output. @@ -81746,7 +81746,7 @@ This can increase performance, and also silently ignore critical errors Save-OBSReplayBuffer : SaveReplayBuffer - 0.2.0.1 + 0.2.1 Saves the contents of the replay buffer output. @@ -81840,7 +81840,7 @@ This can increase performance, and also silently ignore critical errors Save-OBSSourceScreenshot : SaveSourceScreenshot - 0.2.0.1 + 0.2.1 Saves a screenshot of a source to the filesystem. @@ -82108,7 +82108,7 @@ This can increase performance, and also silently ignore critical errors Sends messages to the OBS websocket. - 0.2.0.1 + 0.2.1 Sends one or more messages to the OBS websocket. @@ -82301,7 +82301,7 @@ If -SerialFrame was provied, -StepTime will be the number of frames to wait. Send-OBSCallVendorRequest : CallVendorRequest - 0.2.0.1 + 0.2.1 Call a request registered to a vendor. @@ -82464,7 +82464,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSCustomEvent : BroadcastCustomEvent - 0.2.0.1 + 0.2.1 Broadcasts a `CustomEvent` to all WebSocket clients. Receivers are clients which are identified and subscribed. @@ -82573,7 +82573,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSOffsetMediaInputCursor : OffsetMediaInputCursor - 0.2.0.1 + 0.2.1 Offsets the current cursor position of a media input by the specified value. @@ -82735,7 +82735,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSPauseRecord : PauseRecord - 0.2.0.1 + 0.2.1 Pauses the record output. @@ -82829,7 +82829,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSPressInputPropertiesButton : PressInputPropertiesButton - 0.2.0.1 + 0.2.1 Presses a button in the properties of an input. @@ -82993,7 +82993,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSSleep : Sleep - 0.2.0.1 + 0.2.1 Sleeps for a time duration or number of frames. Only available in request batches with types `SERIAL_REALTIME` or `SERIAL_FRAME`. @@ -83128,7 +83128,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSStreamCaption : SendStreamCaption - 0.2.0.1 + 0.2.1 Sends CEA-608 caption text over the stream output. @@ -83237,7 +83237,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSTriggerHotkeyByKeySequence : TriggerHotkeyByKeySequence - 0.2.0.1 + 0.2.1 Triggers a hotkey using a sequence of keys. @@ -83477,7 +83477,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSTriggerHotkeyByName : TriggerHotkeyByName - 0.2.0.1 + 0.2.1 Triggers a hotkey using its name. See `GetHotkeyList`. @@ -83613,7 +83613,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSTriggerMediaInputAction : TriggerMediaInputAction - 0.2.0.1 + 0.2.1 Triggers an action on a media input. @@ -83774,7 +83774,7 @@ This can increase performance, and also silently ignore critical errors Send-OBSTriggerStudioModeTransition : TriggerStudioModeTransition - 0.2.0.1 + 0.2.1 Triggers the current scene transition. Same functionality as the `Transition` button in studio mode. @@ -83868,7 +83868,7 @@ This can increase performance, and also silently ignore critical errors Sets an OBS 3D Filter. - 0.2.0.1 + 0.2.1 Adds or Changes a 3D Filter on an OBS Input. @@ -84152,7 +84152,7 @@ If this is not provided and the filter already exists, the settings of the filte Adds or sets an audio output source - 0.2.0.1 + 0.2.1 Adds or sets an audio output source in OBS. This captures the audio that is being sent to an output device. @@ -84313,7 +84313,7 @@ To add support for other operating systems, file an issue or open a pull request Sets a browser source - 0.2.0.1 + 0.2.1 Adds or changes a browser source in OBS. @@ -84643,7 +84643,7 @@ If none is provided, this will be the output width of the video settings. Sets a color filter - 0.2.0.1 + 0.2.1 Adds or Changes a Color Correction Filter on an OBS Input. @@ -84921,7 +84921,7 @@ If this is not provided and the filter already exists, the settings of the filte Adds a color source - 0.2.0.1 + 0.2.1 Adds a color source to OBS. This displays a single 32-bit color (RGBA). @@ -85062,7 +85062,7 @@ If no scene name is provided, the current program scene will be used. Set-OBSCurrentPreviewScene : SetCurrentPreviewScene - 0.2.0.1 + 0.2.1 Sets the current preview scene. @@ -85198,7 +85198,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentProfile : SetCurrentProfile - 0.2.0.1 + 0.2.1 Switches to a profile. @@ -85307,7 +85307,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentProgramScene : SetCurrentProgramScene - 0.2.0.1 + 0.2.1 Sets the current program scene. @@ -85442,7 +85442,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentSceneCollection : SetCurrentSceneCollection - 0.2.0.1 + 0.2.1 Switches to a scene collection. @@ -85552,7 +85552,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentSceneTransition : SetCurrentSceneTransition - 0.2.0.1 + 0.2.1 Sets the current scene transition. @@ -85662,7 +85662,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentSceneTransitionDuration : SetCurrentSceneTransitionDuration - 0.2.0.1 + 0.2.1 Sets the duration of the current scene transition, if it is not fixed. @@ -85771,7 +85771,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSCurrentSceneTransitionSettings : SetCurrentSceneTransitionSettings - 0.2.0.1 + 0.2.1 Sets the settings of the current scene transition. @@ -85906,7 +85906,7 @@ This can increase performance, and also silently ignore critical errors Adds a display source - 0.2.0.1 + 0.2.1 Adds a display source to OBS. This captures the contents of the display. @@ -86089,7 +86089,7 @@ If no scene name is provided, the current program scene will be used. Sets a Equalizer filter. - 0.2.0.1 + 0.2.1 Adds or Changes a 3-band Equalizer Filter on an OBS Input. @@ -86216,7 +86216,7 @@ If this is not provided and the filter already exists, the settings of the filte Sets a Gain filter. - 0.2.0.1 + 0.2.1 Adds or Changes a Gain Filter on an OBS Input. @@ -86304,7 +86304,7 @@ If this is not provided and the filter already exists, the settings of the filte Set-OBSInputAudioBalance : SetInputAudioBalance - 0.2.0.1 + 0.2.1 Sets the audio balance of an input. @@ -86465,7 +86465,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputAudioMonitorType : SetInputAudioMonitorType - 0.2.0.1 + 0.2.1 Sets the audio monitor type of an input. @@ -86626,7 +86626,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputAudioSyncOffset : SetInputAudioSyncOffset - 0.2.0.1 + 0.2.1 Sets the audio sync offset of an input. @@ -86787,7 +86787,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputAudioTracks : SetInputAudioTracks - 0.2.0.1 + 0.2.1 Sets the enable state of audio tracks of an input. @@ -86948,7 +86948,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputMute : SetInputMute - 0.2.0.1 + 0.2.1 Sets the audio mute state of an input. @@ -87109,7 +87109,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputName : SetInputName - 0.2.0.1 + 0.2.1 Sets the name of an input (rename). @@ -87270,7 +87270,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputSettings : SetInputSettings - 0.2.0.1 + 0.2.1 Sets the settings of an input. @@ -87457,7 +87457,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSInputVolume : SetInputVolume - 0.2.0.1 + 0.2.1 Sets the volume setting of an input. @@ -87644,7 +87644,7 @@ This can increase performance, and also silently ignore critical errors Sets a markdown source - 0.2.0.1 + 0.2.1 Adds or changes a markdown source in OBS. @@ -87857,7 +87857,7 @@ If none is provided, this will be the output width of the video settings. Set-OBSMediaInputCursor : SetMediaInputCursor - 0.2.0.1 + 0.2.1 Sets the cursor position of a media input. @@ -88019,7 +88019,7 @@ This can increase performance, and also silently ignore critical errors Adds a media source - 0.2.0.1 + 0.2.1 Adds a media source to OBS. @@ -88339,7 +88339,7 @@ If no scene name is provided, the current program scene will be used. Set-OBSOutputSettings : SetOutputSettings - 0.2.0.1 + 0.2.1 Sets the settings of an output. @@ -88474,7 +88474,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSPersistentData : SetPersistentData - 0.2.0.1 + 0.2.1 Sets the value of a "slot" from the selected persistent data realm. @@ -88635,7 +88635,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSProfileParameter : SetProfileParameter - 0.2.0.1 + 0.2.1 Sets the value of a parameter in the current profile's configuration. @@ -88796,7 +88796,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSRecordDirectory : SetRecordDirectory - 0.2.0.1 + 0.2.1 Sets the current directory that the record output writes files to. @@ -88905,7 +88905,7 @@ This can increase performance, and also silently ignore critical errors Sets a RenderDelay filter. - 0.2.0.1 + 0.2.1 Adds or Changes a RenderDelay Filter on an OBS Input. @@ -88993,7 +88993,7 @@ If this is not provided and the filter already exists, the settings of the filte Sets a Scale filter. - 0.2.0.1 + 0.2.1 Adds or Changes a Scale Filter on an OBS Input. @@ -89135,7 +89135,7 @@ This is only valid if the sampling method is set to "lanczos". Set-OBSSceneItemBlendMode : SetSceneItemBlendMode - 0.2.0.1 + 0.2.1 Sets the blend mode of a scene item. @@ -89323,7 +89323,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneItemEnabled : SetSceneItemEnabled - 0.2.0.1 + 0.2.1 Sets the enable state of a scene item. @@ -89511,7 +89511,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneItemIndex : SetSceneItemIndex - 0.2.0.1 + 0.2.1 Sets the index position of a scene item in a scene. @@ -89699,7 +89699,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneItemLocked : SetSceneItemLocked - 0.2.0.1 + 0.2.1 Sets the lock state of a scene item. @@ -89887,7 +89887,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneItemTransform : SetSceneItemTransform - 0.2.0.1 + 0.2.1 Sets the transform and crop info of a scene item. @@ -90074,7 +90074,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneName : SetSceneName - 0.2.0.1 + 0.2.1 Sets the name of a scene (rename). @@ -90235,7 +90235,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSceneSceneTransitionOverride : SetSceneSceneTransitionOverride - 0.2.0.1 + 0.2.1 Sets the scene transition overridden for a scene. @@ -90422,7 +90422,7 @@ This can increase performance, and also silently ignore critical errors Sets a scroll filter. - 0.2.0.1 + 0.2.1 Adds or Changes a Scroll Filter on an OBS Input. @@ -90614,7 +90614,7 @@ If this is not provided and the filter already exists, the settings of the filte Sets a Shader filter. - 0.2.0.1 + 0.2.1 Adds or Changes a Shader Filter on an OBS Input. @@ -90789,7 +90789,7 @@ To see what the name of a shader setting is, change it in the user interface and Sets a Sharpness filter. - 0.2.0.1 + 0.2.1 Adds or Changes a Sharpness Filter on an OBS Input. @@ -90877,7 +90877,7 @@ If this is not provided and the filter already exists, the settings of the filte Sets a Sound Cloud Source - 0.2.0.1 + 0.2.1 Adds or changes a Sound Cloud source OBS. @@ -91440,7 +91440,7 @@ If none is provided, this will be the output width of the video settings. Set-OBSSourceFilterEnabled : SetSourceFilterEnabled - 0.2.0.1 + 0.2.1 Sets the enable state of a source filter. @@ -91627,7 +91627,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSourceFilterIndex : SetSourceFilterIndex - 0.2.0.1 + 0.2.1 Sets the index position of a filter on a source. @@ -91814,7 +91814,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSourceFilterName : SetSourceFilterName - 0.2.0.1 + 0.2.1 Sets the name of a source filter (rename). @@ -92001,7 +92001,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSSourceFilterSettings : SetSourceFilterSettings - 0.2.0.1 + 0.2.1 Sets the settings of a source filter. @@ -92214,7 +92214,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSStreamServiceSettings : SetStreamServiceSettings - 0.2.0.1 + 0.2.1 Sets the current stream service settings (stream destination). @@ -92350,7 +92350,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSStudioModeEnabled : SetStudioModeEnabled - 0.2.0.1 + 0.2.1 Enables or disables studio mode @@ -92459,7 +92459,7 @@ This can increase performance, and also silently ignore critical errors Adds a VLC playlist source - 0.2.0.1 + 0.2.1 Adds or sets VLC playlist sources to OBS. @@ -93048,7 +93048,7 @@ Notice: this current requires confirmation in the UI. Set-OBSTBarPosition : SetTBarPosition - 0.2.0.1 + 0.2.1 Sets the position of the TBar. @@ -93184,7 +93184,7 @@ This can increase performance, and also silently ignore critical errors Set-OBSVideoSettings : SetVideoSettings - 0.2.0.1 + 0.2.1 Sets the current video settings. @@ -93424,7 +93424,7 @@ This can increase performance, and also silently ignore critical errors Adds a VLC playlist source - 0.2.0.1 + 0.2.1 Adds or sets VLC playlist sources to OBS. @@ -93767,7 +93767,7 @@ If an `[IO.FileInfo]` is provided, this will be the exact file. OBS Waveform Source - 0.2.0.1 + 0.2.1 Gets, Sets, or Adds a waveform source in OBS. @@ -94646,7 +94646,7 @@ This is the mathematical function used to determine the current "window" of audi Adds or sets a window capture source - 0.2.0.1 + 0.2.1 Adds or sets a windows capture source in OBS. This captures the contents of a window. @@ -94926,7 +94926,7 @@ This the number of the monitor you would like to capture. Shows content in OBS - 0.2.0.1 + 0.2.1 Shows content in Open Broadcasting Studio @@ -95191,7 +95191,7 @@ Show-OBS -FilePath .\BlueRect.svg Start OBS - 0.2.0.1 + 0.2.1 Starts OBS @@ -95397,7 +95397,7 @@ Show-OBS -FilePath .\BlueRect.svg Starts obs-powershell effects. - 0.2.0.1 + 0.2.1 Starts an effect in OBS PowerShell. @@ -95768,7 +95768,7 @@ If not provided, each effect should use it's own duration. Start-OBSOutput : StartOutput - 0.2.0.1 + 0.2.1 Starts an output. @@ -95877,7 +95877,7 @@ This can increase performance, and also silently ignore critical errors Start-OBSRecord : StartRecord - 0.2.0.1 + 0.2.1 Starts the record output. @@ -95971,7 +95971,7 @@ This can increase performance, and also silently ignore critical errors Start-OBSReplayBuffer : StartReplayBuffer - 0.2.0.1 + 0.2.1 Starts the replay buffer output. @@ -96065,7 +96065,7 @@ This can increase performance, and also silently ignore critical errors Start-OBSStream : StartStream - 0.2.0.1 + 0.2.1 Starts the stream output. @@ -96159,7 +96159,7 @@ This can increase performance, and also silently ignore critical errors Start-OBSVirtualCam : StartVirtualCam - 0.2.0.1 + 0.2.1 Starts the virtualcam output. @@ -96253,7 +96253,7 @@ This can increase performance, and also silently ignore critical errors Stops OBS - 0.2.0.1 + 0.2.1 Stops OBS. @@ -96504,7 +96504,7 @@ Stop-OBS -VirtualCamera -Confirm:$false Stops obs-powershell effects. - 0.2.0.1 + 0.2.1 Stops an effect in OBS PowerShell. @@ -96566,7 +96566,7 @@ Stop-OBS -VirtualCamera -Confirm:$false Stop-OBSOutput : StopOutput - 0.2.0.1 + 0.2.1 Stops an output. @@ -96675,7 +96675,7 @@ This can increase performance, and also silently ignore critical errors Stop-OBSRecord : StopRecord - 0.2.0.1 + 0.2.1 Stops the record output. @@ -96769,7 +96769,7 @@ This can increase performance, and also silently ignore critical errors Stop-OBSReplayBuffer : StopReplayBuffer - 0.2.0.1 + 0.2.1 Stops the replay buffer output. @@ -96863,7 +96863,7 @@ This can increase performance, and also silently ignore critical errors Stop-OBSStream : StopStream - 0.2.0.1 + 0.2.1 Stops the stream output. @@ -96957,7 +96957,7 @@ This can increase performance, and also silently ignore critical errors Stop-OBSVirtualCam : StopVirtualCam - 0.2.0.1 + 0.2.1 Stops the virtualcam output. @@ -97051,7 +97051,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSInputMute : ToggleInputMute - 0.2.0.1 + 0.2.1 Toggles the audio mute state of an input. @@ -97186,7 +97186,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSOutput : ToggleOutput - 0.2.0.1 + 0.2.1 Toggles the status of an output. @@ -97295,7 +97295,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSRecord : ToggleRecord - 0.2.0.1 + 0.2.1 Toggles the status of the record output. @@ -97389,7 +97389,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSRecordPause : ToggleRecordPause - 0.2.0.1 + 0.2.1 Toggles pause on the record output. @@ -97483,7 +97483,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSReplayBuffer : ToggleReplayBuffer - 0.2.0.1 + 0.2.1 Toggles the state of the replay buffer output. @@ -97577,7 +97577,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSStream : ToggleStream - 0.2.0.1 + 0.2.1 Toggles the status of the stream output. @@ -97671,7 +97671,7 @@ This can increase performance, and also silently ignore critical errors Switch-OBSVirtualCam : ToggleVirtualCam - 0.2.0.1 + 0.2.1 Toggles the state of the virtualcam output. @@ -97765,7 +97765,7 @@ This can increase performance, and also silently ignore critical errors Watches OBS - 0.2.0.1 + 0.2.1 Watches the OBS websocket for events. From 7e19c2fb37468a69b339852cce2bf0aa93bf0851 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 1 Jun 2026 18:55:07 +0000 Subject: [PATCH 266/266] release: obs-powershell 0.2.1 Updating Module Manifest and CHANGELOG --- allcommands.ps1 | 92899 +++++++++++++++++++++++----------------------- 1 file changed, 46449 insertions(+), 46450 deletions(-) diff --git a/allcommands.ps1 b/allcommands.ps1 index d7856da5..c898f34a 100644 --- a/allcommands.ps1 +++ b/allcommands.ps1 @@ -2739,450 +2739,515 @@ function Set-OBSSharpnessFilter { #.ExternalHelp obs-powershell-Help.xml -function Get-OBS3dPanelShader { - -[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] -param( -# Set the credits of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('credits')] -[String] -$Credits, -# Set the scale of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, -# Set the tilt_x_deg of OBS3dPanelShader -[Alias('tilt_x_deg')] -[ComponentModel.DefaultBindingProperty('tilt_x_deg')] -[Single] -$TiltXDeg, -# Set the tilt_y_deg of OBS3dPanelShader -[Alias('tilt_y_deg')] -[ComponentModel.DefaultBindingProperty('tilt_y_deg')] -[Single] -$TiltYDeg, -# Set the tilt_z_deg of OBS3dPanelShader -[Alias('tilt_z_deg')] -[ComponentModel.DefaultBindingProperty('tilt_z_deg')] -[Single] -$TiltZDeg, -# Set the pos_x of OBS3dPanelShader -[Alias('pos_x')] -[ComponentModel.DefaultBindingProperty('pos_x')] -[Single] -$PosX, -# Set the pos_y of OBS3dPanelShader -[Alias('pos_y')] -[ComponentModel.DefaultBindingProperty('pos_y')] -[Single] -$PosY, -# Set the thickness of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('thickness')] -[Single] -$Thickness, -# Set the radius_fb of OBS3dPanelShader -[Alias('radius_fb')] -[ComponentModel.DefaultBindingProperty('radius_fb')] -[Single] -$RadiusFb, -# Set the brightness of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('brightness')] -[Single] -$Brightness, -# Set the light_position of OBS3dPanelShader -[Alias('light_position')] -[ComponentModel.DefaultBindingProperty('light_position')] -[Int32] -$LightPosition, -# Set the wiggle of OBS3dPanelShader -[ComponentModel.DefaultBindingProperty('wiggle')] -[Single] -$Wiggle, -# Set the wiggle_rot of OBS3dPanelShader -[Alias('wiggle_rot')] -[ComponentModel.DefaultBindingProperty('wiggle_rot')] -[Management.Automation.SwitchParameter] -$WiggleRot, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - +function Get-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('EffectName')] + [string] + $Name + ) -process { -$shaderName = '3d-panel' -$ShaderNoun = 'OBS3dPanelShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://x.com/HoraiChan/status/1986268258883010766 + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} + } + } -uniform string credits< - string widget_type = "info"; -> = "Based on effect by Horaiken"; + process { -uniform float scale< - string label = "大きさ / Scale"; - string widget_type = "slider"; - float minimum = 0.25; - float maximum = 3.00; - float step = 0.001; -> = 1.0; -uniform float tilt_x_deg< - string label = "縦方向の傾き(X) / Tilt (X)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 20.0; -uniform float tilt_y_deg< - string label = "横方向の傾き(Y) / Tilt (Y)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 35.0; -uniform float tilt_z_deg< - string label = "回転(Z) / Roll (Z)"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.00; - float step = 0.1; -> = 0.0; -uniform float pos_x< - string label = "横位置 / Horizontal Position"; - string widget_type = "slider"; - float minimum = -1.00; - float maximum = 1.00; - float step = 0.0001; -> = 0.0; -uniform float pos_y< - string label = "縦位置 / Vertical Position"; - string widget_type = "slider"; - float minimum = -1.00; - float maximum = 1.00; - float step = 0.0001; -> = 0.0; -uniform float thickness< - string label = "厚み / Thickness"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 0.1; - float step = 0.001; -> = 0.03; -uniform float radius_fb< - string label = "角丸 / Corner Radius"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.2; -uniform float brightness< - string label = "明るさ / Brightness"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 2.00; - float step = 0.01; -> = 1.2; -uniform int light_position < - string label = "照明の位置 / Light Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "左側に光 / Light From Left"; - int option_1_value = 1; - string option_1_label = "右側に光 / Light From Right"; -> = 0; -uniform float wiggle < - string label = "ゆらゆら / Wiggle"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 2.50; - float step = 0.01; -> = 0.0; -uniform bool wiggle_rot < - string label = "角度もゆらゆら / Wiggle Rotation"; ->; + if (-not $Name) { + $script:OBSFX.Values + } elseif ($script:OBSFX[$name]) { + $script:OBSFX[$name] + } + } +} +#.ExternalHelp obs-powershell-Help.xml +function Import-OBSEffect { -float hash1(float n){ return frac(sin(n)*43758.5453123); } + + param( + # The source location of the effect. + # This can be a string, file, directory, command, or module. + [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] + [Alias( + 'FromPath', + 'FromModule', + 'FromScript', + 'FromFunction', + 'FullName', + 'Path', + 'Source' + )] + [ValidateScript({ + $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." + } + return $true + })] + + $From + ) -float noise1D(float x) { - float i = floor(x); - float f = frac(x); - float u = f*f*(3.0 - 2.0*f); - return lerp(hash1(i), hash1(i+1.0), u); // 0..1 -} + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} + } -float fbm1D(float x) { - float v = 0.0; - float a = 0.5; - float f = 1.0; - for(int k=0;k<4;k++){ - v += a * noise1D(x * f); - f *= 2.0; - a *= 0.5; + $newEffects = @() + $obsEffectsPattern = [Regex]::new(' + (?> + ^OBS.(?>fx|effects?)\p{P} + | + [\p{P}-[-]]OBS\.(?>fx|effects?)$ + | + \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ + ) + ','IgnoreCase,IgnorePatternWhitespace') } - return v; -} -float saturate(float x) { return clamp(x, 0.0, 1.0); } + process { + # Since -From can be many things, but a metric has to be a command, + # the purpose of this function is to essentially resolve many things to a command, + # and then cache that command. -float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } -float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } -float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } + # If -From was a string + if ($From -is [string]) { + # It could be a module, so check those first. + :ResolveFromString do { + foreach ($loadedModule in @(Get-Module)) { + # If we find the module, don't try to resolve -From as a path + if ($loadedModule.Name -eq $from) { + # (just set -From again and let the function continue) + $from = $fromModule = $loadedModule;break ResolveFromString + } + + } + # If we think from was a path + $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) + # attempt to resolve it + if ($resolvedPath) { + $from = Get-Item -LiteralPath $resolvedPath + } + } while ($false) + } -// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) -float sdRoundRect2D(float2 p, float2 bxy, float r) { - float2 q = abs(p) - bxy + r; - return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; -} + # If -From is a module + if ($from -is [Management.Automation.PSModuleInfo]) { + # recursively call ourselves with all matching commands + @($from.ExportedCommands.Values) -match $obsEffectsPattern | + Import-OBSEffect + # then, make -From a directory + if ($from.Path) { + $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue + } + } + + # If -From is a directory + if ($from -is [IO.DirectoryInfo]) { + $FromDirectory = $from + # recursively call ourselves with all matching scripts + Get-ChildItem -LiteralPath $from.FullName -Recurse -File | + Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | + Import-OBSEffect + return + } -// 正面シルエット角丸 + Z方向に押し出し -float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { - float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); - float a = sdRoundRect2D(p.xy, b.xy, r_fb); - float dz = abs(p.z) - b.z; - return max(a, dz); -} + # If -From is a file + if ($from -is [IO.FileInfo]) { + # and it matches the naming convention + if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } + # make -From a command. + $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') + } -// 法線 -float3 calcNormal(float3 p, float3 b, float rfb) { - const float e = 0.001; - float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); - float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); - float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); - float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); - return normalize(float3(dx,dy,dz)); -} + # If -From is a command + if ($from -is [Management.Automation.CommandInfo]) { + # decorate the command + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect') + } -// 照明 -float3 shade(float3 n, float3 v) { - float3 l; - if (light_position == 0) { // 左から光 - l = normalize(float3(-1.0, -0.1, 1.0)); - } - else { // 右から光 - l = normalize(float3( 1.0, -0.1, 1.0)); + if ($from -is [Management.Automation.ApplicationInfo]) { + $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' + $newEffect = [PSCustomObject][Ordered]@{ + PSTypeName = 'OBS.PowerShell.Effect' + Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json + EffectName = $effectName + TypeName = $TypeName + } + $script:OBSFX[$effectName] = $newEffect + $newEffects += $newEffect + $newEffect + } else { + if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { + $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') + } + # and add it to our list of new metrics + $newEffects+= $from + $script:OBSFX[$from.EffectName] = $from + $from + } + } } - float diff = saturate(dot(n,l)); - float rim = pow(1.0 - saturate(dot(n,v)), 2.0); - float li = 0.25 + 0.75*diff + 0.08*rim; - return float3(li, li, li); -} - -float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - - // 画面座標(短辺基準) - float aspect = uv_size.x / uv_size.y; - float2 ndc = uv * 2.0 - 1.0; - ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); - float2 p2 = ndc; - p2.x *= aspect; - // カメラ設定 - float3 ro = float3(0.0, 0.0, 3.2); - float3 rd = normalize(float3(p2, -4.0)); - // 回転(Z→Y→X の順に逆回転) - float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); - ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); - rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); - // 画面フィット(短辺基準)+ 厚み - float2 baseXY; - if (aspect > 1.0) { - baseXY = float2(1.0, 1.0 / aspect); - } else { - const float portraitMargin = 0.6; - baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); - } - float3 b = float3(baseXY, thickness) * max(scale, 0.0001); +} - // Wiggle - float diag = length(2.0 * b); - float amp = 0.05 * wiggle * diag; - const float WSPD = 0.1; + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSEffect +{ - // 各軸に独立ノイズ - float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; - float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; - float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; - float3 woff = float3(wx, wy, wz); + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string] + $EffectName + ) - float rotAmp = radians(12.0) * wiggle; - - float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; - float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; - - float3 ro2 = ro; - float3 rd2 = rd; - - if (wiggle_rot) { - ro2 = rotateX(ro2, wobX); - ro2 = rotateY(ro2, wobY); - rd2 = rotateX(rd2, wobX); - rd2 = rotateY(rd2, wobY); + begin { + if (-not $script:OBSFX) { + $script:OBSFX = [Ordered]@{} + } } - float t = 0.0; - float d = 0.0; - bool hit = false; - for (int i=0; i<64; i++) { - float3 pos = ro2 + rd2 * t; - d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); - if (d < 0.001) { hit = true; break; } - t += d; - if (t > 8.0) break; + process { + if ($script:OBSFX[$name]) { + $script:OBSFX.Stop() + $script:OBSFX.Remove($name) + } } +} + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSEffect +{ + + [CmdletBinding(PositionalBinding=$false)] + param( + # The name of the effect. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + $effectNames = @(Get-OBSEffect| + Select-Object -Unique -ExpandProperty EffectName) + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($effectNames -replace '^', "'" -replace '$',"'") + } + })] + [Parameter(Mandatory)] + [string[]] + $EffectName, - // ヒットしなければ完全透明(元ソースは非表示) - if (!hit) return float4(0.0, 0.0, 0.0, 0.0); - - float3 pos = ro2 + rd2 * t; - float3 pObj = pos - woff; - float3 n = calcNormal(pObj, b, radius_fb); - float3 vdir = normalize(-rd2); + # The duration of the effect. + # If provided, all effects should use this duration. + # If not provided, each effect should use it's own duration. + [Timespan] + $Duration, - // テクスチャ貼り付け - float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); - float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; + # The parameters passed to the effect. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('EffectParameters')] + [Collections.IDictionary] + $EffectParameter = @{}, - // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) - float4 texFront = image.Sample(textureSampler, uvTex); - float4 texEdge = image.Sample(textureSampler, uvTex); - float4 tex = lerp(texEdge, texFront, frontMask); + # The arguments passed to the effect. + [Parameter(ValueFromRemainingArguments)] + [Alias('EffectArguments')] + [PSObject[]] + $EffectArgument = @(), - // フロント面エッジ・ハイライト(細い線) - float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); - float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF - float edgeWidth = 0.02 * min(b.x, b.y); - float edgeIntensity = 0.6; - float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); - float edgeMask = frontMask * edgeProx; - tex.rgb *= (1.0 + edgeMask * edgeIntensity); + # If provided, will step thru running + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ticks')] + [int] + $Step, - // 照明 - float3 lightTerm = shade(n, vdir); - tex.rgb *= lightTerm; + # The SceneItemID. If this is provided, the effect will be given a target. + [Parameter(ValueFromPipelineByPropertyName)] + [int] + $SceneItemID, - // 明るさスライダ - tex.rgb *= brightness; + # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $SceneName, - // 出力 - return float4(tex.rgb, 1.0); -} + # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $FilterName, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) + # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $SourceName, + + # If set, will loop the effect. + [switch] + $Loop, + + # If provided, will loop the effect a number of times. + [int] + $LoopCount, + + # If set, will bounce the effect (flip it / reverse it) + [switch] + $Bounce, + + # If set, will reverse an effect. + [switch] + $Reverse + ) + + process { + foreach ($NameOfEffect in $EffectName) { + $obsEffect = Get-OBSEffect -EffectName $NameOfEffect + + if (-not $obsEffect) { + Write-Warning "No Effect named '$NameOfEffect'" + continue } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + if ($LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + if ($loop -or $Bounce) { + $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force + if (-not $LoopCount) { + $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force } - continue nextParameter - } - } + } else { + $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force + } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + if ($Reverse) { + $obsEffect.Reversed = $true + } + + if ($obsEffect -isnot [Management.Automation.CommandInfo]) { + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + $obsEffect.Start() + + } else { + if ($step -and $obsEffect.Messages) { + $obsEffect.Step($step) + continue + } + + if (-not $this) { + if ($_.pstypenames -like '*.GetSourceFilter*') { + $this = $_ + } elseif ($FilterName -and $SourceName) { + $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName + } + + if ($_.pstypenames -like '*.GetSceneItem*') { + $this = $_ + } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { + $this = + foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { + if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { + $sceneItem;break + } + elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { + $sceneItem;break + } + } + } + } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + if ($Duration -and $obsEffect.Parameters.Duration) { + $EffectParameter.Duration = $Duration + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument + if ($obsEffectOutput) { + $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force + if ($step) { + $obsEffect.Step($step) + } else { + $obsEffect.Start() + } + } } + $obsEffect } + + + } +} +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSEffect +{ + + param( + # The name of the effect. + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [string] + $EffectName) - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + process { + $obsEffect = Get-OBSEffect -EffectName $EffectName + + if (-not $obsEffect) { return } + + $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force + } +} +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSInput { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] +[Alias('obs.powershell.websocket.CreateInput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -3191,269 +3256,213 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBS3dSwapTransitionShader { +function Add-OBSProfile { -[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] +[Alias('obs.powershell.websocket.CreateProfile')] param( -# Set the image_a of OBS3dSwapTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBS3dSwapTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBS3dSwapTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBS3dSwapTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the reflection of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('reflection')] -[Single] -$Reflection, -# Set the perspective of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('perspective')] -[Single] -$Perspective, -# Set the depth of OBS3dSwapTransitionShader -[ComponentModel.DefaultBindingProperty('depth')] -[Single] -$Depth, -# Set the background_color of OBS3dSwapTransitionShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[String] -$BackgroundColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = '3d_swap_transition' -$ShaderNoun = 'OBS3dSwapTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MlXGzf -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; -uniform float reflection< - string label = "Reflection (0.4)"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.4; -uniform float perspective< - string label = "Perspective (0.2)"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = .2; -uniform float depth< - string label = "Depth (3.0)"; - string widget_type = "slider"; - float minimum = 1.00; - float maximum = 10.00; - float step = 0.1; -> = 3.; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#ifndef OPENGL -#define lessThan(a,b) (a < b) -#endif + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; - -bool inBounds (float2 p) { - return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); -} - -float2 project (float2 p) { - return p * float2(1.0, -1.2) + float2(0.0, 2.22); -} - -float4 bgColor (float2 p, float2 pfr, float2 pto) { - float4 c = background_color; - pfr = project(pfr); - if (inBounds(pfr)) { - c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); - } - pto = project(pto); - if (inBounds(pto)) { - c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); - } - return c; -} - -float4 mainImage(VertData v_in) : TARGET { - float2 p = v_in.uv; - float2 pfr = float2(-1.,-1.); - float2 pto = float2(-1.,-1.); - - float progress = transition_time; - float size = lerp(1.0, depth, progress); - float persp = perspective * progress; - pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); - - size = lerp(1.0, depth, 1.-progress); - persp = perspective * (1.-progress); - pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); - - bool fromOver = progress < 0.5; - float4 rgba = background_color; - if (fromOver) { - if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); - } - else if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else { - rgba = bgColor(p, pfr, pto); - } - } - else { - if (inBounds(pto)) { - rgba = image_b.Sample(textureSampler, pto); - } - else if (inBounds(pfr)) { - rgba = image_a.Sample(textureSampler, pfr); - } - else { - rgba = bgColor(p, pfr, pto); - } - } - if (convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Add-OBSScene { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] +[Alias('obs.powershell.websocket.CreateScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -3462,147 +3471,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAddShader { +function Add-OBSSceneCollection { -[Alias('Set-OBSAddShader','Add-OBSAddShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] +[Alias('obs.powershell.websocket.CreateSceneCollection')] param( -# Set the other_image of OBSAddShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] -[String] -$OtherImage, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Add' -$ShaderNoun = 'OBSAddShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return clamp(base + other, 0.0, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -3611,182 +3578,127 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaBorderShader { +function Add-OBSSceneItem { -[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] +[Alias('obs.powershell.websocket.CreateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Add-OBSSceneSource')] param( -# Set the border_color of OBSAlphaBorderShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_thickness of OBSAlphaBorderShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the alpha_cut_off of OBSAlphaBorderShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -process { -$shaderName = 'alpha_border' -$ShaderNoun = 'OBSAlphaBorderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 border_color< - string label = "Border color"; -> = {0.0,0.0,0.0,1.0}; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -float4 mainImage(VertData v_in) : TARGET -{ - float4 pix = image.Sample(textureSampler, v_in.uv); - if (pix.a > alpha_cut_off) - return pix; - [loop] for(int x = -border_thickness;x alpha_cut_off) - return border_color; - } - } - } - return pix; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -3795,300 +3707,125 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAlphaGamingBentCameraShader { +function Add-OBSSourceFilter { -[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] +[Alias('obs.powershell.websocket.CreateSourceFilter')] param( -# Set the left_side_width of OBSAlphaGamingBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSAlphaGamingBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSAlphaGamingBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSAlphaGamingBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSAlphaGamingBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] -[Single] -$RightSideShadow, -# Set the right_flip_width of OBSAlphaGamingBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] -[Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] -[Single] -$RightFlipShadow, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'alpha-gaming-bent-camera' -$ShaderNoun = 'OBSAlphaGamingBentCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; - -uniform float right_side_width< - string label = "Right side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; - } - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; - } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -4097,270 +3834,230 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedPathShader { +function Copy-OBSSceneItem { -[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] +[Alias('obs.powershell.websocket.DuplicateSceneItem')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedPathShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedPathShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedPathShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedPathShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedPathShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the speed_percent of OBSAnimatedPathShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the path_map of OBSAnimatedPathShader -[Alias('path_map')] -[ComponentModel.DefaultBindingProperty('path_map')] -[String] -$PathMap, -# Set the reverse of OBSAnimatedPathShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneName')] +[string] +$DestinationSceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] +[string] +$DestinationSceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'animated_path' -$ShaderNoun = 'OBSAnimatedPathShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform int speed_percent = 100; -uniform texture2d path_map; -uniform bool reverse = false; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 convert_pmalpha(float4 c) -{ - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - float3 pos = v_in.pos.xyz; - float3 current_pos; - float speed = speed_percent * 0.01; - //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float t = 1.0 + sin(elapsed_time * speed) ; - // combine luma texture and user defined shine color - float luma = path_map.Sample(textureSampler, v_in.uv).x; - if (reverse) - { - luma = 1.0 - luma; - } - float time = lerp(0.0f, 1.0f , t - 1.0); +} - // set current position in time - current_pos.x = 0; - current_pos.y = 0; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentPreviewScene { - float2 offset = uv_offset; - if (speed == 0.0f) - { - offset.x = 0.0f; - offset.y = 0.0f; - } - else - { - offset.x = uv_offset.x + time * luma; - offset.y = uv_offset.y + time * luma; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} +process { -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -4369,403 +4066,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAnimatedTextureShader { +function Get-OBSCurrentProgramScene { -[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] +[Alias('obs.powershell.websocket.GetCurrentProgramScene')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAnimatedTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAnimatedTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAnimatedTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAnimatedTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAnimatedTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSAnimatedTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Image of OBSAnimatedTextureShader -[Alias('Animation_Image')] -[ComponentModel.DefaultBindingProperty('Animation_Image')] -[String] -$AnimationImage, -# Set the Colorization_Image of OBSAnimatedTextureShader -[Alias('Colorization_Image')] -[ComponentModel.DefaultBindingProperty('Colorization_Image')] -[String] -$ColorizationImage, -# Set the reverse of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the bounce of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('bounce')] -[Management.Automation.SwitchParameter] -$Bounce, -# Set the center_animation of OBSAnimatedTextureShader -[Alias('center_animation')] -[ComponentModel.DefaultBindingProperty('center_animation')] -[Management.Automation.SwitchParameter] -$CenterAnimation, -# Set the polar_animation of OBSAnimatedTextureShader -[Alias('polar_animation')] -[ComponentModel.DefaultBindingProperty('polar_animation')] -[Management.Automation.SwitchParameter] -$PolarAnimation, -# Set the polar_angle of OBSAnimatedTextureShader -[Alias('polar_angle')] -[ComponentModel.DefaultBindingProperty('polar_angle')] -[Single] -$PolarAngle, -# Set the polar_height of OBSAnimatedTextureShader -[Alias('polar_height')] -[ComponentModel.DefaultBindingProperty('polar_height')] -[Single] -$PolarHeight, -# Set the speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] -[Single] -$SpeedHorizontalPercent, -# Set the speed_vertical_percent of OBSAnimatedTextureShader -[Alias('speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] -[Single] -$SpeedVerticalPercent, -# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader -[Alias('tint_speed_horizontal_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] -[Single] -$TintSpeedHorizontalPercent, -# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader -[Alias('tint_speed_vertical_percent')] -[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] -[Single] -$TintSpeedVerticalPercent, -# Set the Alpha of OBSAnimatedTextureShader -[ComponentModel.DefaultBindingProperty('Alpha')] -[Single] -$Alpha, -# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader -[Alias('Use_Animation_Image_Color')] -[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] -[Management.Automation.SwitchParameter] -$UseAnimationImageColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'animated_texture' -$ShaderNoun = 'OBSAnimatedTextureShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 -// Animates a texture with polar sizing and color options -// for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes; - -uniform texture2d Animation_Image; -uniform texture2d Colorization_Image; -uniform bool reverse = false; -uniform bool bounce = false; -uniform bool center_animation = true; -uniform bool polar_animation = true; -uniform float polar_angle = 90.0; -uniform float polar_height = 1.0; -uniform float speed_horizontal_percent = 50; -uniform float speed_vertical_percent = 5; -uniform float tint_speed_horizontal_percent = 50; -uniform float tint_speed_vertical_percent = 5; -uniform float Alpha = 1.0; -uniform bool Use_Animation_Image_Color = true; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -float4 convert_pmalpha(float4 color) -{ - float4 ret = color; - if (color.a >= 0.001) - ret.xyz /= color.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} - -float2 time(float2 speed_dir) -{ - float PI = 3.1415926535897932384626433832795; //acos(-1); - float2 t = (elapsed_time * speed_dir) ; - if (bounce) - { - // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen - t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; - t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; - t *= -0.5; - } - if (reverse) - t = t * -1; - return t; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -VertData mainTransform(VertData v_in) -{ - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - VertData vert_out; - //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float2 offset = uv_offset; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - if (center_animation) - { - vert_out.uv = v_in.uv - 0.5f; - } - else - { - offset += time(speed_dir); - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + offset; - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return vert_out; } -float4 mainImage(VertData v_in) : TARGET -{ - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - - float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); - float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); - - //compensate for background vertex shader values - float2 background_offset = float2(-.5,-.5); - if (!center_animation) - background_offset = time(speed_dir); - float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); - - // Convert our texture coordinates to polar form: - if (polar_animation) { - - float2 polar = float2( - atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle - log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius - ); - - // Check how much our texture sampling point changes between - // neighbouring pixels to the sides (ddx) and above/below (ddy) - ///float4 gradient = float4(ddx(polar), ddy(polar)); +} - // If our angle wraps around between adjacent samples, - // discard one full rotation from its value and keep the fraction. - ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSCurrentSceneTransition { - float2 tintUVs = polar * 4; - tintUVs += time(tint_speed_dir); - // Apply texture scale - polar *= 4; - // Scroll the texture over time. - polar += time(speed_dir); - float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); - +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); - float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); - //animation.a = clamp((intensity),0.0,1.0); - if (Use_Animation_Image_Color) - { - animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - else - { - animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; - } - //if (keyAmount > 0.5f) - rgba = lerp(rgba, animation, animation.a * Alpha); - } - return rgba; -} +process { -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -4774,256 +4272,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAsciiShader { +function Get-OBSCurrentSceneTransitionCursor { -[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] +[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the scale of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('scale')] -[Int32] -$Scale, -# Set the base_color of OBSAsciiShader -[Alias('base_color')] -[ComponentModel.DefaultBindingProperty('base_color')] -[String] -$BaseColor, -# Set the monochrome of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('monochrome')] -[Management.Automation.SwitchParameter] -$Monochrome, -# Set the character_set of OBSAsciiShader -[Alias('character_set')] -[ComponentModel.DefaultBindingProperty('character_set')] -[Int32] -$CharacterSet, -# Set the note of OBSAsciiShader -[ComponentModel.DefaultBindingProperty('note')] -[String] -$Note, -# The name of the source. This must be provided when adding an item for the first time +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'ascii' -$ShaderNoun = 'OBSAsciiShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 -// https://github.com/Oncorporation/obs-shaderfilter -// Based on the following shaders: -// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 -// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 - -// Modifications of original shaders include: -// - Porting from GLSL to HLSL -// - Combining characters sets from both source shaders -// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set -// -// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ -// converts a bitmap into int then decodes it to look like text - -uniform int scale< - string label = "Scale"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; // Size of characters -uniform float4 base_color< - string label = "Base color"; -> = {0.0,1.0,0.0,1.0}; // Monochrome base color -uniform bool monochrome< - string label = "Monochrome"; -> = false; -uniform int character_set< - string label = "Character set"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Large set of non-letters"; - int option_1_value = 1; - string option_1_label = "Small set of capital letters"; -> = 0; -uniform string note< - string widget_type = "info"; -> = "Base color is used as monochrome base color."; - -float character(int n, float2 p) -{ - p = floor(p*float2(4.0, 4.0) + 2.5); - if (clamp(p.x, 0.0, 4.0) == p.x) - { - if (clamp(p.y, 0.0, 4.0) == p.y) - { - int a = int(round(p.x) + 5.0 * round(p.y)); - if (((n >> a) & 1) == 1) return 1.0; - } - } - return 0.0; -} - -float2 mod(float2 x, float2 y) -{ - return x - y * floor(x/y); -} - -float4 mainImage( VertData v_in ) : TARGET -{ - float2 iResolution = uv_size; - float2 pix = v_in.uv * iResolution; - float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); - - float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; - - int n; - int charset = clamp(character_set, 0, 1); - - if (charset==0) - { - if (gray <= 0.2) n = 4096; // . - if (gray > 0.2) n = 65600; // : - if (gray > 0.3) n = 332772; // * - if (gray > 0.4) n = 15255086; // o - if (gray > 0.5) n = 23385164; // & - if (gray > 0.6) n = 15252014; // 8 - if (gray > 0.7) n = 13199452; // @ - if (gray > 0.8) n = 11512810; // # - } - else if (charset==1) - { - if (gray <= 0.1) n = 0; - if (gray > 0.1) n = 9616687; // R - if (gray > 0.3) n = 32012382; // S - if (gray > 0.5) n = 16303663; // D - if (gray > 0.7) n = 15255086; // O - if (gray > 0.8) n = 16301615; // B - } - float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); - - if (monochrome) - { - c.rgb = base_color.rgb; - } - c = c*character(n, p); - - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5032,272 +4375,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAspectRatioShader { +function Get-OBSGroup { -[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] +[Alias('obs.powershell.websocket.GetGroupList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSAspectRatioShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSAspectRatioShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSAspectRatioShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSAspectRatioShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSAspectRatioShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSAspectRatioShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the borderColor of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('borderColor')] -[String] -$BorderColor, -# Set the notes of OBSAspectRatioShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'aspect_ratio' -$ShaderNoun = 'OBSAspectRatioShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// variables -uniform float4 borderColor = {0,0,0,0}; -float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; -uniform string notes; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float2 hw = uv_scale; - // determine the game window''s current aspect ratio - float windowaspect = hw.x / hw.y; +} - // current viewport height should be scaled by this amount - float scaleheight = windowaspect / targetaspect; +} - // if scaled height is less than current height, add letterbox - if (scaleheight < 1.0f) - { - Rect rect = camera.rect; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGroupSceneItem { - rect.width = 1.0f; - rect.height = scaleheight; - rect.x = 0; - rect.y = (1.0f - scaleheight) / 2.0f; - camera.rect = rect; - } - else // add pillarbox - { - float scalewidth = 1.0f / scaleheight; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] +[Alias('obs.powershell.websocket.GetGroupSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - Rect rect = camera.rect; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - rect.width = scalewidth; - rect.height = 1.0f; - rect.x = (1.0f - scalewidth) / 2.0f; - rect.y = 0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - camera.rect = rect; - } - return vert_out; -} -float4 mainImage(VertData v_in) : TARGET -{ - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) - { - return borderColor; - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} +process { -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5306,190 +4591,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSAudioShader { +function Get-OBSHotkey { -[Alias('Set-OBSAudioShader','Add-OBSAudioShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] +[Alias('obs.powershell.websocket.GetHotkeyList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the audio_peak of OBSAudioShader -[Alias('audio_peak')] -[ComponentModel.DefaultBindingProperty('audio_peak')] -[Single] -$AudioPeak, -# Set the audio_magnitude of OBSAudioShader -[Alias('audio_magnitude')] -[ComponentModel.DefaultBindingProperty('audio_magnitude')] -[Single] -$AudioMagnitude, -# Set the intensity of OBSAudioShader -[ComponentModel.DefaultBindingProperty('intensity')] -[Single] -$Intensity, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'audio' -$ShaderNoun = 'OBSAudioShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Audio shader example showing the difference between audio_peak and audio_magnitude. -// Left half uses audio_peak (red), right half uses audio_magnitude (blue). -uniform float audio_peak; -uniform float audio_magnitude; - -uniform float intensity < - string label = "Audio intensity"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 3.0; - float step = 0.1; -> = 1.0; - -float4 mainImage(VertData v_in) : TARGET { - float4 color = image.Sample(textureSampler, v_in.uv); - - // Split screen based on UV coordinate - if (v_in.uv.x < 0.5) { - // Left half: audio_peak (instantaneous spikes, more reactive) - // Tint with red to show peak activity. - float peak_strength = audio_peak * intensity; - float3 peak_color = color.rgb + float3(peak_strength, 0, 0); - return float4(peak_color, color.a); - } else { - // Right half: audio_magnitude (RMS/averaged levels, smoother) - // Tint with blue to show magnitude activity. - float mag_strength = audio_magnitude * intensity; - float3 mag_color = color.rgb + float3(0, 0, mag_strength); - return float4(mag_color, color.a); - } -} -/* -EXPLANATION: -- audio_peak: Shows instantaneous maximum levels, very responsive to drums/percussion. -- audio_magnitude: Shows RMS (Root Mean Square) levels, smoother and represents sustained audio. - -TYPICAL BEHAVIOR: -- With music containing drums: Left side (peak) will flash more dramatically on beats. -- With sustained tones: Right side (magnitude) will show more consistent levels. -- Peak reacts faster to sudden sounds, magnitude is more stable for smooth effects. -*/ -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5498,315 +4694,219 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBackgroundRemovalShader { +function Get-OBSInput { -[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] +[Alias('obs.powershell.websocket.GetInputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSBackgroundRemovalShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSBackgroundRemovalShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSBackgroundRemovalShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSBackgroundRemovalShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSBackgroundRemovalShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSBackgroundRemovalShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the target of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('target')] -[String] -$Target, -# Set the color of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('color')] -[String] -$Color, -# Set the opacity of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, -# Set the invert of OBSBackgroundRemovalShader -[ComponentModel.DefaultBindingProperty('invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the Convert_709to601 of OBSBackgroundRemovalShader -[Alias('Convert_709to601')] -[ComponentModel.DefaultBindingProperty('Convert_709to601')] -[Management.Automation.SwitchParameter] -$Convert709to601, -# Set the Convert_601to709 of OBSBackgroundRemovalShader -[Alias('Convert_601to709')] -[ComponentModel.DefaultBindingProperty('Convert_601to709')] -[Management.Automation.SwitchParameter] -$Convert601to709, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'background_removal' -$ShaderNoun = 'OBSBackgroundRemovalShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; -uniform texture2d target; -uniform float4 color; -uniform float opacity = 15.0; -uniform bool invert; -uniform bool Convert_709to601; -uniform bool Convert_601to709; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float dot(float3 a,float3 b){ - return a.x*b.x+a.y*b.y+a.z*b.z; } -//BT.601 to BT.709 -// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input -// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. -float4 Convert601to709(float4 rgba) -{ - float3 s1 = rgba.rgb; - s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace - return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace -} -//BT.709 to BT.601 -float4 Convert709to601(float4 rgba) -{ - float3 s1 = rgba.rgb; - s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); - return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; -} +} -VertDataOut VSDefault(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); - vert_out.uv = v_in.uv; - vert_out.uv2 = v_in.uv * uv_scale + uv_offset; - return vert_out; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioBalance { -float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET -{ - float Tolerance = opacity * 0.01; - float4 rgba = image.Sample(textureSampler, v_in.uv); - float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; - if (invert){ - targetRGB.rgb = 1.0 - targetRGB.rgb; - } - if (Convert_709to601) - { - rgba.rgb = Convert709to601(rgba).rgb; - targetRGB.rgb = Convert709to601(targetRGB).rgb; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] +[Alias('obs.powershell.websocket.GetInputAudioBalance')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - if (Convert_601to709) - { - rgba.rgb = Convert601to709(rgba).rgb; - targetRGB.rbg = Convert601to709(targetRGB).rgb; - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - float4 shadowRGB = targetRGB * targetRGB; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if ((abs(targetRGB.r - rgba.r) <= Tolerance && - abs(targetRGB.g - rgba.g) <= Tolerance && - abs(targetRGB.b - rgba.b) <= Tolerance) - || (abs(shadowRGB.r - rgba.r) <= Tolerance && - abs(shadowRGB.g - rgba.g) <= Tolerance && - abs(shadowRGB.b - rgba.b) <= Tolerance)) - { - rgba.rgba = float4(0,0,0,0); - } - return rgba; -} -technique Draw -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSColorMaskRGBA(v_in); - } -} +process { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -5815,255 +4915,224 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlendOpacityShader { +function Get-OBSInputAudioMonitorType { -[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Vertical of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the Rotational of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Rotational')] -[Management.Automation.SwitchParameter] -$Rotational, -# Set the Rotation_Offset of OBSBlendOpacityShader -[Alias('Rotation_Offset')] -[ComponentModel.DefaultBindingProperty('Rotation_Offset')] -[Single] -$RotationOffset, -# Set the Opacity_Start_Percent of OBSBlendOpacityShader -[Alias('Opacity_Start_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] -[Single] -$OpacityStartPercent, -# Set the Opacity_End_Percent of OBSBlendOpacityShader -[Alias('Opacity_End_Percent')] -[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] -[Single] -$OpacityEndPercent, -# Set the Spread of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Spread')] -[Single] -$Spread, -# Set the Speed of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Notes of OBSBlendOpacityShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'blend_opacity' -$ShaderNoun = 'OBSBlendOpacityShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 14, 2022 -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.01; -> = 0.0; -uniform float Opacity_Start_Percent< - string label = "Opacity Start Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 0.0; -uniform float Opacity_End_Percent< - string label = "Opacity End Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform float Spread< - string label = "Spread"; - string widget_type = "slider"; - float minimum = 0.25; - float maximum = 10.0; - float step = 0.01; -> = 0.5; -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.0; -uniform bool Apply_To_Alpha_Layer = true; -uniform string Notes< - string widget_type = "info"; -> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; -float4 mainImage(VertData v_in) : TARGET -{ - float4 point_color = image.Sample(textureSampler, v_in.uv); - float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; - float4 gray = float4(luminance,luminance,luminance, 1); - float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) - { - //set opacity and direction - float opacity = (-1 * lPos.x) * 0.5; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - if (Rotational && (Vertical == false)) - { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; - } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - if (Vertical && (Rotational == false)) - { - opacity = (-1 * lPos.y) * 0.5; - } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - opacity += time; - opacity = frac(opacity); - point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); - } - return point_color; } +} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSInputAudioSyncOffset { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6072,152 +5141,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBlinkShader { +function Get-OBSInputAudioTracks { -[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] +[Alias('obs.powershell.websocket.GetInputAudioTracks')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the speed of OBSBlinkShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'blink' -$ShaderNoun = 'OBSBlinkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - return float4(color.r, color.g, color.b, color.a * (1 + sin(t)) / 2); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6226,218 +5254,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBloomShader { +function Get-OBSInputDefaultSettings { -[Alias('Set-OBSBloomShader','Add-OBSBloomShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] +[Alias('obs.powershell.websocket.GetInputDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Angle_Steps of OBSBloomShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSBloomShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSBloomShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the notes of OBSBloomShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputKind')] +[string] +$InputKind, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'bloom' -$ShaderNoun = 'OBSBloomShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Bloom shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; // -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; // -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 2.0; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - int radiusSteps = clamp(Radius_Steps, 0, 20); - int angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - float minRadius = (0.0 * uv_pixel_interval.y); - float maxRadius = (10.0 * uv_pixel_interval.y); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 outputPixel = c0; - float4 accumulatedColor = float4(0,0,0,0); - - int totalSteps = radiusSteps * angleSteps; - float angleDelta = (2.0 * PI) / float(angleSteps); - float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); - - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + float(radiusStep) * radiusDelta; - - for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { - float2 currentCoord; - - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor =image.Sample(textureSampler, currentCoord); - float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); - - accumulatedColor += currentFraction * currentColor / float(totalSteps); - - } - } - - outputPixel += accumulatedColor * ampFactor; - return outputPixel; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6446,151 +5362,106 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBorderShader { +function Get-OBSInputKind { -[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] +[Alias('obs.powershell.websocket.GetInputKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the borderColor of OBSBorderShader -[ComponentModel.DefaultBindingProperty('borderColor')] -[String] -$BorderColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('unversioned')] +[switch] +$Unversioned, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'border' -$ShaderNoun = 'OBSBorderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4 borderColor; -float4 mainImage(VertData v_in) : TARGET -{ - if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) - { - return borderColor; - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6599,237 +5470,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBoxBlurShader { +function Get-OBSInputMute { -[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] +[Alias('obs.powershell.websocket.GetInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Strength of OBSBoxBlurShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSBoxBlurShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSBoxBlurShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSBoxBlurShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSBoxBlurShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'box-blur' -$ShaderNoun = 'OBSBoxBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - float transparent = 0.0; - int count = 1; - float samples = 0.0; - float4 c = float4(0.0, 0.0, 0.0, 0.0); - float Steps = float(Strength); - - [loop] for (int i = -Strength; i <= Strength; i++) { - [loop] for (int k = -Strength; k <= Strength; k++) { - float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); - transparent += sc.a; - count++; - c += sc * sc.a; - samples += sc.a; - } - } - if(samples > 0.0) - c /= samples; - c.a = transparent / float(count); - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -6838,227 +5583,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBulgePinchShader { +function Get-OBSInputPropertiesListPropertyItems { -[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] +[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the radius of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the magnitude of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('magnitude')] -[Single] -$Magnitude, -# Set the center_x of OBSBulgePinchShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSBulgePinchShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the animate of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the notes of OBSBulgePinchShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'BulgePinch' -$ShaderNoun = 'OBSBulgePinchShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.0; -uniform float magnitude< - string label = "Magnitude"; - string widget_type = "slider"; - float minimum = -1.3333; - float maximum = 1.3333; - float step = 0.01; -> = 0.0; -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.01; -> = 0.25; -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.01; -> = 0.25; -uniform bool animate = false; - -uniform string notes< - string widget_type = "info"; -> = "Distorts the screen, expanding or drawing in pixels around a point." - - -float4 mainImage(VertData v_in) : TARGET -{ - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; - - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); - float percent = dist/radius; - if(anim_mag > 0) - v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); - else - v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); - v_out.uv += (2 * center); - v_out.uv.x *= ar; - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7067,343 +5701,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSBurnShader { +function Get-OBSInputSettings { -[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] +[Alias('obs.powershell.websocket.GetInputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Burn_Gradient of OBSBurnShader -[Alias('Burn_Gradient')] -[ComponentModel.DefaultBindingProperty('Burn_Gradient')] -[String] -$BurnGradient, -# Set the Speed of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Gradient_Adjust of OBSBurnShader -[Alias('Gradient_Adjust')] -[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] -[Single] -$GradientAdjust, -# Set the Dissolve_Value of OBSBurnShader -[Alias('Dissolve_Value')] -[ComponentModel.DefaultBindingProperty('Dissolve_Value')] -[Single] -$DissolveValue, -# Set the Animated of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the Apply_to_Channel of OBSBurnShader -[Alias('Apply_to_Channel')] -[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the Apply_Smoke of OBSBurnShader -[Alias('Apply_Smoke')] -[ComponentModel.DefaultBindingProperty('Apply_Smoke')] -[Management.Automation.SwitchParameter] -$ApplySmoke, -# Set the Smoke_Horizonal_Speed of OBSBurnShader -[Alias('Smoke_Horizonal_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] -[Single] -$SmokeHorizonalSpeed, -# Set the Smoke_Vertical_Speed of OBSBurnShader -[Alias('Smoke_Vertical_Speed')] -[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] -[Single] -$SmokeVerticalSpeed, -# Set the Iterations of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Iterations')] -[Int32] -$Iterations, -# Set the Notes of OBSBurnShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'burn' -$ShaderNoun = 'OBSBurnShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//for use with obs-shaderfilter 1.0 -//Converted to OpenGL by Exeldro February 17, 2022 -float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} -float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} -float4 permute(float4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} -float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} -float2 fade(float2 t) { - return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); -} - -float dot(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} - -// Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} -// Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0; - float4 gy = abs(gx) - 0.5; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x, gy.x); - float2 g10 = float2(gx.y, gy.y); - float2 g01 = float2(gx.z, gy.z); - float2 g11 = float2(gx.w, gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - -uniform texture2d Burn_Gradient = "burngradient.png"; -uniform float Speed = 0.33; -uniform float Gradient_Adjust = 0.85; -uniform float Dissolve_Value = 1.43; -uniform bool Animated; -uniform bool Apply_to_Channel; -uniform bool Apply_Smoke = true; -uniform float Smoke_Horizonal_Speed = 0.3; -uniform float Smoke_Vertical_Speed = 0.17; -uniform int Iterations = 4; -uniform string Notes< - string widget_type = "info"; -> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - float4 smoke = float4(1.0,1.0,1.0,1.0); - float4 result = smoke; - float t = elapsed_time * Speed; - float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay - float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); - //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); - - float perlin = 0.5; - float smoke_perlin = 0; - float scale = 1; - float w = 0.5; - - for (int i = 0; i < Iterations; i++) { - //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; - float2 coord = (v_in.uv + t * (dir * .1)) * scale; - float2 period = scale * dir; - perlin += pnoise(coord, period) * w; - if (Apply_Smoke) - smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; - - scale *= 2.0; - w *= 0.5; - } - - //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); - if (!Animated) - cycle = 1; - float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); - float overOne = saturate(d * Gradient_Adjust); - float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); - - if (Apply_to_Channel) { - result = c * burn; - } - else { - result = float4(perlin, perlin, perlin, 1.0) * burn; - } - if (smoke_perlin > 0) { - smoke *= smoke_perlin; - if (result.a <= 0.04) - result = float4(smoke.rgb, smoke_perlin); - result += smoke; - } - return result; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7412,286 +5814,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCartoonShader { +function Get-OBSInputVolume { -[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] +[Alias('obs.powershell.websocket.GetInputVolume')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the ViewProj of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSCartoonShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSCartoonShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSCartoonShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSCartoonShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSCartoonShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSCartoonShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the notes of OBSCartoonShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the hue_steps of OBSCartoonShader -[Alias('hue_steps')] -[ComponentModel.DefaultBindingProperty('hue_steps')] -[Int32] -$HueSteps, -# Set the value_steps of OBSCartoonShader -[Alias('value_steps')] -[ComponentModel.DefaultBindingProperty('value_steps')] -[Int32] -$ValueSteps, -# Set the Apply_To_Alpha_Layer of OBSCartoonShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cartoon' -$ShaderNoun = 'OBSCartoonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform string notes = "5/2 seems reasonable"; -uniform int hue_steps = 5; -uniform int value_steps = 2; -uniform bool Apply_To_Alpha_Layer = true; -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -VertDataOut VSDefault(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv; - return vert_out; -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float3 rgb2hsv(float3 c) -{ - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } -float3 hsv2rgb(float3 c) -{ - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} -float fit(float v, int factor) -{ - return round(v * factor) / factor; -} +} -float hue_wrap(float h) -{ - return fmod(h + 1, 2) - 1; - if(h > 1) - return h - 2; - if(h < -1) - return h + 2; - return h; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSLastReplayBufferReplay { -float4 PassThrough(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) - { - float3 hsv = rgb2hsv(rgba.rgb); - hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); - //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); - rgba = float4(hsv2rgb(hsv), rgba.a); - //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); - } - return rgba; -} -technique Draw -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PassThrough(v_in); - } -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] +[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +process { - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7700,217 +6030,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCellShadedShader { +function Get-OBSMediaInputStatus { -[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] +[Alias('obs.powershell.websocket.GetMediaInputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Angle_Steps of OBSCellShadedShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSCellShadedShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the notes of OBSCellShadedShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cell_shaded' -$ShaderNoun = 'OBSCellShadedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 5; -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 9; -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 2.0; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (3 * uv_pixel_interval.y); - float maxRadius = (24 * uv_pixel_interval.y); - - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); - - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; - for (float angle=0; angle <(2*PI); angle += angleDelta) { - float2 currentCoord; - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps; - - } - } - accumulatedColor *= ampFactor; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - return c0 - accumulatedColor; // Cell shaded style -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } +} - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSMonitor { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] +[Alias('obs.powershell.websocket.GetMonitorList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -7919,376 +6246,317 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaticAberrationShader { +function Get-OBSOutput { -[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] +[Alias('obs.powershell.websocket.GetOutputList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the power of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# Set the gamma of OBSChromaticAberrationShader -[ComponentModel.DefaultBindingProperty('gamma')] -[Single] -$Gamma, -# Set the num_iter of OBSChromaticAberrationShader -[Alias('num_iter')] -[ComponentModel.DefaultBindingProperty('num_iter')] -[Int32] -$NumIter, -# Set the distort_radial of OBSChromaticAberrationShader -[Alias('distort_radial')] -[ComponentModel.DefaultBindingProperty('distort_radial')] -[Management.Automation.SwitchParameter] -$DistortRadial, -# Set the distort_barrel of OBSChromaticAberrationShader -[Alias('distort_barrel')] -[ComponentModel.DefaultBindingProperty('distort_barrel')] -[Management.Automation.SwitchParameter] -$DistortBarrel, -# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader -[Alias('offset_spectrum_ycgco')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] -[Management.Automation.SwitchParameter] -$OffsetSpectrumYcgco, -# Set the offset_spectrum_yuv of OBSChromaticAberrationShader -[Alias('offset_spectrum_yuv')] -[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] -[Management.Automation.SwitchParameter] -$OffsetSpectrumYuv, -# Set the use_random of OBSChromaticAberrationShader -[Alias('use_random')] -[ComponentModel.DefaultBindingProperty('use_random')] -[Management.Automation.SwitchParameter] -$UseRandom, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'chromatic-aberration' -$ShaderNoun = 'OBSChromaticAberrationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/XssGz8 -//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 0.01; -uniform float gamma< - string label = "Gamma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3.0; - float step = 0.01; -> = 2.2; -uniform int num_iter< - string label = "Number iterations"; - string widget_type = "slider"; - int minimum = 3; - int maximum = 25; - int step = 1; -> = 7; -uniform bool distort_radial = false; -uniform bool distort_barrel = false; -uniform bool offset_spectrum_ycgco = false; -uniform bool offset_spectrum_yuv = false; -uniform bool use_random = true; -float2 remap( float2 t, float2 a, float2 b ) { - return clamp( (t - a) / (b - a), 0.0, 1.0 ); -} -float3 spectrum_offset_rgb( float t ) -{ - float t0 = 3.0 * t - 1.5; - float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); - return ret; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float3 lin2srgb( float3 c ) -{ - return pow( c, float3(gamma, gamma, gamma) ); -} -float3 srgb2lin( float3 c ) -{ - return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 yCgCo2rgb(float3 ycc) -{ - float R = ycc.x - ycc.y + ycc.z; - float G = ycc.x + ycc.y; - float B = ycc.x - ycc.y - ycc.z; - return float3(R,G,B); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float3 spectrum_offset_ycgco( float t ) -{ - //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink - //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple - float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange - //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue - return yCgCo2rgb( ygo ); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 yuv2rgb( float3 yuv ) -{ - float3 rgb; - rgb.r = yuv.x + yuv.z * 1.13983; - rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); - rgb.b = yuv.x + yuv.y * 2.03211; - return rgb; } -float2 radialdistort(float2 coord, float2 amt) -{ - float2 cc = coord - 0.5; - return coord + 2.0 * cc * amt; -} -float2 barrelDistortion( float2 p, float2 amt ) -{ - p = 2.0 * p - 1.0; +} - /* - const float maxBarrelPower = 5.0; - //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper - float theta = atan(p.y, p.x); - float2 radius = float2( length(p) ); - radius = pow(radius, 1.0 + maxBarrelPower * amt); - p.x = radius.x * cos(theta); - p.y = radius.y * sin(theta); + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSOutputSettings { - /*/ - // much faster version - //const float maxBarrelPower = 5.0; - //float radius = length(p); - float maxBarrelPower = sqrt(5.0); - float radius = dot(p,p); //faster but doesn''t match above accurately - p *= pow(float2(radius, radius), maxBarrelPower * amt); - /* */ - return p * 0.5 + 0.5; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] +[Alias('obs.powershell.websocket.GetOutputSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float2 brownConradyDistortion(float2 uv, float dist) -{ - uv = uv * 2.0 - 1.0; - // positive values of K1 give barrel distortion, negative give pincushion - float barrelDistortion1 = 0.1 * dist; // K1 in text books - float barrelDistortion2 = -0.025 * dist; // K2 in text books +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float r2 = dot(uv,uv); - uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; - //uv *= 1.0 + barrelDistortion1 * r2; - - // tangential distortion (due to off center lens elements) - // is not modeled in this function, but if it was, the terms would go here - return uv * 0.5 + 0.5; -} -float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) -{ - float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); - //float2 dist = mix( min_distort, max_distort, t ); - if (distort_radial) - return radialdistort( uv, 2.0 * dist ); - - if(distort_barrel) - return barrelDistortion( uv, 1.75 * dist ); //distortion at center - return brownConradyDistortion( uv, 75.0 * dist.x ); -} +process { -// ==== -float3 spectrum_offset_yuv( float t ) -{ - //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green - //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green - float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange - //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue - return yuv2rgb( yuv ); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 spectrum_offset( float t ) -{ - if(offset_spectrum_ycgco) - return spectrum_offset_ycgco( t ); - if(offset_spectrum_yuv) - return spectrum_offset_yuv( t ); - return spectrum_offset_rgb( t ); - //return srgb2lin( spectrum_offset_rgb( t ) ); - //return lin2srgb( spectrum_offset_rgb( t ) ); } -float4 mainImage(VertData v_in) : TARGET -{ - float2 max_distort = float2(power, power); - float2 min_distort = 0.5 * max_distort; - float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); +} - float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); - - //debug oversiz - //float2 distuv = distort( uv, 1.0, max_distort ); - //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) - //{ - // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; - //} - - - float stepsiz = 1.0 / (float(num_iter)-1.0); - float rnd = 0.0; - if(use_random) - rnd = rand_f; - - float t = rnd * stepsiz; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSOutputStatus { - float3 sumcol = float3(0.0, 0.0, 0.0); - float3 sumw = float3(0.0, 0.0, 0.0); - float colA = 0.0; - - for ( int i=0; iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] +[Alias('obs.powershell.websocket.GetOutputStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8297,205 +6565,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSChromaUVDistortionShader { +function Get-OBSPersistentData { -[Alias('Set-OBSChromaUVDistortionShader','Add-OBSChromaUVDistortionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] +[Alias('obs.powershell.websocket.GetPersistentData')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the distortion of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('distortion')] -[Single] -$Distortion, -# Set the amplitude of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the chroma of OBSChromaUVDistortionShader -[ComponentModel.DefaultBindingProperty('chroma')] -[Single] -$Chroma, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Chroma+UV-Distortion' -$ShaderNoun = 'OBSChromaUVDistortionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/WsdyRN - -//Higher values = less distortion -uniform float distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 5.0; - float maximum = 1000.0; - float step = 0.01; -> = 75.; -//Higher values = tighter distortion -uniform float amplitude< - string label = "Amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 10.; -//Higher values = more color distortion -uniform float chroma< - string label = "Chroma"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 6.28318531; - float step = 0.01; -> = .5; - -float2 zoomUv(float2 uv, float zoom) { - float2 uv1 = uv; - uv1 += .5; - uv1 += zoom/2.-1.; - uv1 /= zoom; - return uv1; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 uvt = v_in.uv; - - float2 uvtR = uvt; - float2 uvtG = uvt; - float2 uvtB = uvt; - - //Uncomment the following line to get varying chroma distortion - //chroma = sin(elapsed_time)/2.+.5; - - uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); - uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); - uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); - - float2 uvR = zoomUv(uvtR, 1.1); - float2 uvG = zoomUv(uvtG, 1.1); - float2 uvB = zoomUv(uvtB, 1.1); - - float4 colR = image.Sample(textureSampler, uvR); - float4 colG = image.Sample(textureSampler, uvG); - float4 colB = image.Sample(textureSampler, uvB); - return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8504,238 +6678,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCircleMaskFilterShader { +function Get-OBSProfile { -[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] +[Alias('obs.powershell.websocket.GetProfileList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Radius of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Radius')] -[Single] -$Radius, -# Set the Circle_Offset_X of OBSCircleMaskFilterShader -[Alias('Circle_Offset_X')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] -[Int32] -$CircleOffsetX, -# Set the Circle_Offset_Y of OBSCircleMaskFilterShader -[Alias('Circle_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] -[Int32] -$CircleOffsetY, -# Set the Source_Offset_X of OBSCircleMaskFilterShader -[Alias('Source_Offset_X')] -[ComponentModel.DefaultBindingProperty('Source_Offset_X')] -[Int32] -$SourceOffsetX, -# Set the Source_Offset_Y of OBSCircleMaskFilterShader -[Alias('Source_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] -[Int32] -$SourceOffsetY, -# Set the Antialiasing of OBSCircleMaskFilterShader -[ComponentModel.DefaultBindingProperty('Antialiasing')] -[Management.Automation.SwitchParameter] -$Antialiasing, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - - -process { -$shaderName = 'circle-mask-filter' -$ShaderNoun = 'OBSCircleMaskFilterShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Circle Mask Filter version 1.01, for OBS Shaderfilter -// Copyright 2022 by SkeletonBow -// Twitter: -// Twitch: -// License: GNU GPLv2 -// -// Changelog: -// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. -// 1.0 - Initial release - -uniform float Radius< - string label = "Radius"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform int Circle_Offset_X< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Circle_Offset_Y< - string label = "Circle Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0; -uniform int Source_Offset_X< - string label = "Source Offset X"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0.0; -uniform int Source_Offset_Y< - string label = "Source Offset Y"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; -> = 0.0; +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform bool Antialiasing = true; -#define Smoothness 100.00 -#define AAwidth 4 -#define uv_pi uv_pixel_interval +process { -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; - float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; - float radius = Radius * 0.01; - float smwidth = radius * Smoothness * 0.01; - - float4 obstex = image.Sample( textureSampler, uv - soffset); - float4 color = obstex; - // Account for aspect ratio - uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; - float2 cuv = 0.5 + coffset; - float dist = distance(cuv,uv); - // Anti-aliased or pixelated edge - if( Antialiasing ) { - color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); - } else { - color.a = step( dist, radius ); - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - return float4(color.rgb, color.a); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSProfileParameter { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] +[Alias('obs.powershell.websocket.GetProfileParameter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -8744,346 +6894,307 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockAnalogShader { +function Get-OBSRecordDirectory { -[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] +[Alias('obs.powershell.websocket.GetRecordDirectory')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_ms of OBSClockAnalogShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockAnalogShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockAnalogShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockAnalogShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the hour_handle_color of OBSClockAnalogShader -[Alias('hour_handle_color')] -[ComponentModel.DefaultBindingProperty('hour_handle_color')] -[Single[]] -$HourHandleColor, -# Set the minute_handle_color of OBSClockAnalogShader -[Alias('minute_handle_color')] -[ComponentModel.DefaultBindingProperty('minute_handle_color')] -[Single[]] -$MinuteHandleColor, -# Set the second_handle_color of OBSClockAnalogShader -[Alias('second_handle_color')] -[ComponentModel.DefaultBindingProperty('second_handle_color')] -[Single[]] -$SecondHandleColor, -# Set the outline_color of OBSClockAnalogShader -[Alias('outline_color')] -[ComponentModel.DefaultBindingProperty('outline_color')] -[Single[]] -$OutlineColor, -# Set the top_line_color of OBSClockAnalogShader -[Alias('top_line_color')] -[ComponentModel.DefaultBindingProperty('top_line_color')] -[Single[]] -$TopLineColor, -# Set the background_color of OBSClockAnalogShader -[Alias('background_color')] -[ComponentModel.DefaultBindingProperty('background_color')] -[Single[]] -$BackgroundColor, -# Set the time_offset_hours of OBSClockAnalogShader -[Alias('time_offset_hours')] -[ComponentModel.DefaultBindingProperty('time_offset_hours')] -[Int32] -$TimeOffsetHours, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_analog' -$ShaderNoun = 'OBSClockAnalogShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Based on https://www.shadertoy.com/view/XdKXzy -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform float3 hour_handle_color = {1.0,1.0,1.0}; -uniform float3 minute_handle_color = {1.0,1.0,1.0}; -uniform float3 second_handle_color = {1.0,0.0,0.0}; -uniform float3 outline_color = {1.0,1.0,1.0}; -uniform float3 top_line_color = {1.0,0.0,0.0}; -uniform float3 background_color = {.5,.5,.5}; -uniform int time_offset_hours = 0; - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif -// this is my first try to actually use glsl almost from scratch -// so far all i''ve done is learning by doing / reading glsl docs. -// this is inspired by my non glsl „elapsed_time“ projects -// especially this one: https://www.gottz.de/analoguhr.htm - -// i will most likely use a buffer in future to calculate the elapsed_time -// aswell as to draw the background of the clock only once. -// tell me if thats a bad idea. -// update: -// screenshot: http://i.imgur.com/dF0nHDk.png -// as soon as i think its in a usefull state i''ll release the source -// of that particular c++ application on github. -// i hope sommeone might find it usefull :D -#define PI 3.141592653589793238462643383 + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// from https://www.shadertoy.com/view/4s3XDn <3 -float ln(float2 p, float2 a, float2 b) -{ - float2 pa = p - a; - float2 ba = b - a; - float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * h); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -// i think i should spend some elapsed_time reading docs in order to minimize this. -// hints apreciated -// (Rotated LiNe) -float rln(float2 uv, float start, float end, float perc) { - float inp = perc * PI * 2.0; - float2 coord = float2(sin(inp), cos(inp)); - return ln(uv, coord * start, coord * end); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -// i need this to have an alphachannel in the output -// i intend to use an optimized version of this shader for a transparent desktop widget experiment -float4 mixer(float4 c1, float4 c2) { - // please tell me if you think this would boost performance. - // the elapsed_time i implemented mix myself it sure did reduce - // the amount of operations but i''m not sure now - // if (c2.a <= 0.0) return c1; - // if (c2.a >= 1.0) return c2; - return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); - // in case you are curious how you could implement mix yourself: - // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); -} - -float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { - if (dist <= width + shadow) { - // lets draw the shadow - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); - // now lets draw the antialiased handle - color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); - } - return color; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 R = uv_size; - // calculate the size of a pixel - float px = 1.0 / R.y; - // create percentages of the coordinate system - float2 p = (v_in.uv * uv_size).xy / R; - // center the scene and add perspective - float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); - - /*float2 uv = -1.0 + 2.0 * p.xy; - // lets add perspective for mobile device support - if (uv_size.x > uv_size.y) - uv.x *= uv_size.x / uv_size.y; - else - uv.y *= uv_size.y / uv_size.x;*/ - - // lets scale the scene a bit down: - uv *= 1.1; - px *= 0.9; - - float width = 0.015; - float dist = 1.0; - float centerdist = length(uv); - - float4 color = image.Sample(textureSampler, v_in.uv); - - // background of the clock - if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); - - float isRed = 1.0; - - if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { - // minute bars - for (float i = 0.0; i <= 15.0; i += 1.0) { - if (mod(i, 5.0) == 0.0) { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); - // draw first bar red - if (i == 0.0 && uv.y > 0.0) { - isRed = dist; - dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); - color = mixer(color, float4(top_line_color, dist)); - dist = 1.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } } - else { - dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); - } } - - // outline circle - dist = min(dist, abs(1.0-width-length(uv))); - // draw clock shadow - if (centerdist > 1.0) - color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); - - // draw outline + minute bars in white - color = mixer(color, float4(0.0, 0.0, 0.0, - (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); - color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - if (centerdist < 1.0) { - float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); - // hour - color = styleHandle(color, px, - rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), - hour_handle_color, 0.03, 0.02); + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - // minute - color = styleHandle(color, px, - rln(uv, -0.075, 0.7, elapsed_time / 3600.0), - minute_handle_color, 0.02, 0.02); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // second - color = styleHandle(color, px, - min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), - second_handle_color, 0.01, 0.02); - } - - - return color; } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSRecordStatus { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] +[Alias('obs.powershell.websocket.GetRecordStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSReplayBufferStatus { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] +[Alias('obs.powershell.websocket.GetReplayBufferStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -9092,381 +7203,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalLedShader { +function Get-OBSScene { -[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] +[Alias('obs.powershell.websocket.GetSceneList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_sec of OBSClockDigitalLedShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalLedShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalLedShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the showMatrix of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showMatrix')] -[Management.Automation.SwitchParameter] -$ShowMatrix, -# Set the showOff of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('showOff')] -[Management.Automation.SwitchParameter] -$ShowOff, -# Set the ampm of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ampm')] -[Management.Automation.SwitchParameter] -$Ampm, -# Set the ledColor of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('ledColor')] -[String] -$LedColor, -# Set the offsetHours of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalLedShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] -[Int32] -$OffsetSeconds, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_digital_led' -$ShaderNoun = 'OBSClockDigitalLedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/MdfGzf -// cmarangu has linked all 7 segments in his comments -// https://www.shadertoy.com/view/3dtSRj - -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#endif -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform bool showMatrix = false; -uniform bool showOff = false; -uniform bool ampm = false; -uniform float4 ledColor = {1.0,0,0,1.0}; -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float segment(float2 uv, bool On) -{ - if (!On && !showOff) - return 0.0; - - float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* - (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); - - //Fiddle with lights and matrix - //uv.x += sin(elapsed_time*60.0*6.26)/14.0; - //uv.y += cos(elapsed_time*60.0*6.26)/14.0; - - //led like brightness - if (On){ - seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; - } else { - seg *= -(0.05+length(uv*float2(0.2,0.1))); - } - return seg; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float sevenSegment(float2 uv,int num) -{ - float seg= 0.0; - seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); - seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); - seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); - seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); - seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); - seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); - seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); - - return seg; -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float showNum(float2 uv,int nr, bool zeroTrim) -{ - //Speed optimization, leave if pixel is not in segment - if (abs(uv.x)>1.5 || abs(uv.y)>1.2) - return 0.0; - - float seg= 0.0; - if (uv.x>0.0) - { - nr /= 10; - if (nr==0 && zeroTrim) - nr = -1; - seg += sevenSegment(uv+float2(-0.75,0.0),nr); - } else { - seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - return seg; } -float dots(float2 uv) -{ - float seg = 0.0; - uv.y -= 0.5; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - uv.y += 1.0; - seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); - return seg; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / - min(uv_size.x,uv_size.y); - - if (uv_size.x>uv_size.y) - { - uv *= 6.0; - } - else - { - uv *= 12.0; - } - - uv.x *= -1.0; - uv.x += uv.y/12.0; - //wobble - //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; - //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; - uv.x += 3.5; - float seg = 0.0; +} - if(timeMode == 0){ - seg += showNum(uv,current_time_sec,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - seg += showNum(uv,current_time_min,false); - uv.x -= 1.75; - seg += dots(uv); - uv.x -= 1.75; - if (ampm) { - if(current_time_hour == 0){ - seg += showNum(uv,12,true); - }else if(current_time_hour > 12){ - seg += showNum(uv,current_time_hour-12,true); - }else{ - seg += showNum(uv,current_time_hour,true); - } - } else { - seg += showNum(uv,current_time_hour,true); - } - }else{ - float timeSecs = 0.0; - if(timeMode == 1){ - timeSecs = elapsed_time_enable; - }else if(timeMode == 2){ - timeSecs = elapsed_time_active; - }else if(timeMode == 3){ - timeSecs = elapsed_time_show; - }else if(timeMode == 4){ - timeSecs = elapsed_time_start; - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneCollection { - timeSecs += offsetSeconds + offsetHours*3600; - if(timeSecs < 0) - timeSecs = 0.9999-timeSecs; - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - - uv.x -= 1.75; - seg += dots(uv); - - uv.x -= 1.75; - - seg += showNum(uv,int(mod(timeSecs,60.0)),false); - - timeSecs = floor(timeSecs/60.0); - if (ampm) - { - if(timeSecs == 0.0){ - timeSecs = 12.0; - }else if (timeSecs > 12.0){ - timeSecs = mod(timeSecs,12.0); - } - }else if (timeSecs > 24.0) { - timeSecs = mod(timeSecs,24.0); - } - - uv.x -= 1.75; - - seg += dots(uv); - - uv.x -= 1.75; - seg += showNum(uv,int(mod(timeSecs,60.0)),true); - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] +[Alias('obs.powershell.websocket.GetSceneCollectionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - - if (seg==0.0){ - return image.Sample(textureSampler, v_in.uv); - } - // matrix over segment - if (showMatrix) - { - seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); - //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); - } - if (seg<0.0) - { - seg = -seg;; - return float4(seg,seg,seg,1.0); - } - return float4(ledColor.rgb * seg, ledColor.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +process { - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -9475,658 +7409,349 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSClockDigitalNixieShader { +function Get-OBSSceneItem { -[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] +[Alias('obs.powershell.websocket.GetSceneItemList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the current_time_ms of OBSClockDigitalNixieShader -[Alias('current_time_ms')] -[ComponentModel.DefaultBindingProperty('current_time_ms')] -[Int32] -$CurrentTimeMs, -# Set the current_time_sec of OBSClockDigitalNixieShader -[Alias('current_time_sec')] -[ComponentModel.DefaultBindingProperty('current_time_sec')] -[Int32] -$CurrentTimeSec, -# Set the current_time_min of OBSClockDigitalNixieShader -[Alias('current_time_min')] -[ComponentModel.DefaultBindingProperty('current_time_min')] -[Int32] -$CurrentTimeMin, -# Set the current_time_hour of OBSClockDigitalNixieShader -[Alias('current_time_hour')] -[ComponentModel.DefaultBindingProperty('current_time_hour')] -[Int32] -$CurrentTimeHour, -# Set the timeMode of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('timeMode')] -[Int32] -$TimeMode, -# Set the offsetHours of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetHours')] -[Int32] -$OffsetHours, -# Set the offsetSeconds of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('offsetSeconds')] -[Int32] -$OffsetSeconds, -# Set the corecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('corecolor')] -[Single[]] -$Corecolor, -# Set the halocolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('halocolor')] -[Single[]] -$Halocolor, -# Set the flarecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('flarecolor')] -[Single[]] -$Flarecolor, -# Set the anodecolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodecolor')] -[Single[]] -$Anodecolor, -# Set the anodehighlightscolor of OBSClockDigitalNixieShader -[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] -[Single[]] -$Anodehighlightscolor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'clock_digital_nixie' -$ShaderNoun = 'OBSClockDigitalNixieShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/fsBcRm -uniform int current_time_ms; -uniform int current_time_sec; -uniform int current_time_min; -uniform int current_time_hour; -uniform int timeMode< - string label = "Time mode"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Time"; - int option_1_value = 1; - string option_1_label = "Enable duration"; - int option_2_value = 2; - string option_2_label = "Active duration"; - int option_3_value = 3; - string option_3_label = "Show duration"; - int option_4_value = 4; - string option_4_label = "Load duration"; -> = 0; -uniform int offsetHours = 0; -uniform int offsetSeconds = 0; -// Colors as named variables, if you want to tweak them -uniform float3 corecolor = {1.0,0.7,0.0}; -uniform float3 halocolor = {1.0,0.5,0.0}; -uniform float3 flarecolor = {1.0,0.3,0.0}; -uniform float3 anodecolor = {0.2,0.1,0.1}; -uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#ifndef OPENGL -#define mod(x,y) (x - y * floor(x / y)) -#define lessThan(a,b) (a < b) -#define greaterThan(a,b) (a > b) -#endif - -// psrdnoise (c) Stefan Gustavson and Ian McEwan, -// ver. 2021-12-02, published under the MIT license: -// https://github.com/stegu/psrdnoise/ -float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) -{ - float2 uv = float2(x.x+x.y*0.5, x.y); - float2 i0 = floor(uv), f0 = frac(uv); - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - float2 i1 = i0 + o1, i2 = i0 + 1.0; - float2 v0 = float2(i0.x - i0.y*0.5, i0.y); - float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); - float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); - float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; - float3 iu, iv, xw, yw; - if(any(greaterThan(period, float2(0.0,0.0)))) { - xw = float3(v0.x, v1.x, v2.x); - yw = float3(v0.y, v1.y, v2.y); - if(period.x > 0.0) - xw = mod(float3(v0.x, v1.x, v2.x), period.x); - if(period.y > 0.0) - yw = mod(float3(v0.y, v1.y, v2.y), period.y); - iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); - } else { - iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); - } - float3 hash = mod(iu, 289.0); - hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); - hash = mod((hash*34.0 + 10.0)*hash, 289.0); - float3 psi = hash*0.07482 + alpha; - float3 gx = cos(psi); float3 gy = sin(psi); - float2 g0 = float2(gx.x, gy.x); - float2 g1 = float2(gx.y, gy.y); - float2 g2 = float2(gx.z, gy.z); - float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); - w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; - float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); - float n = dot(w4, gdotx); - float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; - float2 dn0 = w4.x*g0 + dw.x*x0; - float2 dn1 = w4.y*g1 + dw.y*x1; - float2 dn2 = w4.z*g2 + dw.z*x2; - gradient = 10.9*(dn0 + dn1 + dn2); - return 10.9*n; -} - -// Compute the shortest distance from p -// to a line segment from p1 to p2. -float lined(float2 p1, float2 p2, float2 p) { - float2 p1p2 = p2 - p1; - float2 v = normalize(p1p2); - float2 s = p - p1; - float t = dot(v, s); - if (t<0.0) return length(s); - if (t>length(p1p2)) return length(p - p2); - return length(s - t*v); -} - -// Compute the shortest distance from p to a circle -// with center at c and radius r. (Extremely simple.) -float circled(float2 c, float r, float2 p) { - return abs(length(p - c) - r); -} - -// Compute the shortest distance from p to a -// circular arc with center c from p1 to p2. -// p1, p2 are in the +angle direction (ccw), -// to resolve the major/minor arc ambiguity, so -// specifying p1, p2 in the wrong order will -// yield the complement to the arc you wanted. -// If p1 = p2, the entire circle is drawn, but -// you don''t want to use this function to draw -// a circle. Use the simple circled() instead. -// If p1 and p2 have different distances to c, -// the end of the arc will not look right. If -// this is inconvenient, uncomment the 3rd line. -float arcd(float2 c, float2 p1, float2 p2, float2 p) { - - float2 v1 = p1 - c; - float2 v2 = p2 - c; - // Optional: make sure p1, p2 are both on the circle - // v2 = normalize(v2)*length(v1); - float2 v = p - c; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi - if(all(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); // nearest end - } else { - return abs(length(v) - length(v1)); // dist to arc + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - } else { // Arc angle > pi - if(any(lessThan(float2(0.0,0.0), w))) { - return min(length(p1-p), length(p2-p)); + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - return abs(length(v) - length(v1)); + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} -// A convenient anti-aliased step() using auto derivatives -float aastep(float threshold, float value) { - float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); - return smoothstep(threshold-afwidth, threshold+afwidth, value); } -// A smoothstep() that blends to an aastep() under minification -float aasmoothstep(float t1, float t2, float v) { - float aw = 0.7 * length(float2(ddx(v), ddy(v))); - float sw = max(0.5*(t2-t1), aw); - float st = 0.5*(t1+t2); - return smoothstep(st-sw, st+sw, v); -} -// Distance field of a hexagonal (simplex) grid -// The return vector contains the distances to the -// three closest points, sorted by magnitude. -float3 hexgrid(float2 p) { +} - const float stretch = 1.0/0.8660; - - // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) - p.y = p.y * stretch; - // Transform to grid space (axis-aligned "simplex" grid) - float2 uv = float2(p.x + p.y*0.5, p.y); - // Determine which simplex we''re in, with i0 being the "base" - float2 i0 = floor(uv); - float2 f0 = frac(uv); - // o1 is the offset in simplex space to the second corner - float cmp = step(f0.y, f0.x); - float2 o1 = float2(cmp, 1.0-cmp); - // Enumerate the remaining simplex corners - float2 i1 = i0 + o1; - float2 i2 = i0 + float2(1.0, 1.0); - // Transform corners back to texture space - float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); - float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); - float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); - float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); - // Only three values - bubble sort is just fine. - d.yz = (d.y < d.z) ? d.yz : d.zy; - d.xy = (d.x < d.y) ? d.xy : d.yx; - d.yz = (d.y < d.z) ? d.yz : d.zy; - return d; -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemBlendMode { -// The digits. Simple functions, only a lot of them. -// These glyphs and their implementation as distance fields -// are the original work of me (stefan.gustavson@gmail.com), -// and the code below is released under the MIT license: -// https://opensource.org/licenses/MIT -// (If that is inconvenient for you, let me know. I''m reasonable.) -// -// Experts say mortals should not attempt to design character shapes. -// "It''s just ten simple digits", I thought, "How hard can it be?" -// A week later, after countless little tweaks to proportions and -// curvature, and with a notepad full of sketches and pen-and-paper -// math, some of it horribly wrong because it was decades since I -// solved this kind of equations by hand, I know the answer: -// It can be *really* hard. But also loads of fun! -// -float nixie0(float2 p) { - // Special hack instead of pasting together arcs and lines - float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); - return abs(d - 2.0); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float nixie1(float2 p) { - float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); - float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); - return min(d1, d2); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -float nixie1alt(float2 p) { // Straight line - return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -float nixie2(float2 p) { - const float x = 3.2368345; // Icky coordinates, - const float y = 4.4283002; // used twice below - float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); - float d2 = arcd(float2(10.657842, -5.001899), // Also icky - float2(x, y), float2(-0.25, 0.0), p); - float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float nixie2alt(float2 p) { // Straight neck - float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); - float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); - float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); - return min(min(d1, d2), d3); -} -float nixie3(float2 p) { - // Two round parts: - // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); - // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); - // Angled top, more like classic Nixie tube digits: - float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); - float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); - return min(min(d1, d2), d3); -} +process { -float nixie3alt(float2 p) { // Two round parts of the same size - float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); - float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); - return min(d1, d2); -} -float nixie4(float2 p) { - // This digit is 5.0 units wide, most others are 4.0 or 4.5 - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); - return min(min(d1, d2), d3); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float nixie4alt(float2 p) { - // This digit is 4.0 units wide, but looks cropped - float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); - float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(d1, d2), d3); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float nixie5(float2 p) { - float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); - float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); - float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); - float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); - return min(min(d1, d2), min(d3, d4)); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float nixie5alt(float2 p) { - float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); - float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); - float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); - float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); - float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); - float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); - return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float nixie6(float2 p) { - float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); - float d2 = circled(float2(2.0, 2.25), 2.25, p); - return min(d1, d2); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float nixie6alt(float2 p) { // Straight neck - float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); - float d2 = circled(float2(2.0,2.0), 2.0, p); - return min(d1, d2); } -float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact - float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); - float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); - return min(min(d1, d2), d3); -} -float nixie7alt(float2 p) { // Straight neck - float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); - float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); - return min(d1, d2); -} +} -float nixie8(float2 p) { - float d1 = circled(float2(2.0, 2.2), 2.2, p); - float d2 = circled(float2(2.0, 6.2), 1.8, p); - return min(d1, d2); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemEnabled { -float nixie8alt(float2 p) { // Same size loops - float d1 = circled(float2(2.0,2.0), 2.0, p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} -float nixie9(float2 p) { - float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); - float d2 = circled(float2(2.0, 5.75), 2.25, p); - return min(d1, d2); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] +[Alias('obs.powershell.websocket.GetSceneItemEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -float nixie9alt(float2 p) { // Straight neck - float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); - float d2 = circled(float2(2.0, 6.0), 2.0, p); - return min(d1, d2); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -float nixieminus(float2 p) { - return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -float nixieequals(float2 p) { - float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); - float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); - return min(d1, d2); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float nixieplus(float2 p) { - float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); - float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); - return min(d1, d2); -} -float nixiedot(float2 p) { - // circled with r=0 yields a point, but with more work - return length(p - float2(2.0, 0.0)); -} +process { -float nixiecolon(float2 p) { - float d1 = length(p - float2(2.0,2.0)); - float d2 = length(p - float2(2.0, 5.0)); - return min(d1, d2); -} -// End of MIT-licensed code + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float number(int n, float2 p) { - switch(n) { - case 0: return nixie0(p); - case 1: return nixie1(p); - case 2: return nixie2(p); - case 3: return nixie3(p); - case 4: return nixie4(p); - case 5: return nixie5(p); - case 6: return nixie6(p); - case 7: return nixie7(p); - case 8: return nixie8(p); - case 9: return nixie9(p); - default: return 1e10; - } -} -// Display the current time with a retro Nixie-tube look -// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 -// All code in the "Image" tab is public domain. -// Functions in the "Common" tab are also public domain, -// except where a separate license is specified. -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = ((float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size)/uv_size.x); - float time = 0.0; - if(timeMode == 0){ - time = float(current_time_hour*3600+current_time_min*60+current_time_sec) + float(current_time_ms)/1000.0; - }else if(timeMode == 1){ - time = elapsed_time_enable; - }else if(timeMode == 2){ - time = elapsed_time_active; - }else if(timeMode == 3){ - time = elapsed_time_show; - }else if(timeMode == 4){ - time = elapsed_time_start; - } - time += offsetSeconds + offsetHours * 3600; - if(time < 0) - time = 0.9999-time; - float2 p = -3.0+uv*50.0 - float2(0.0,9.0); - - float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), - max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); - - // Some relief for the GPU: exit early if we''re in the black margins - if(bbox == 0.0) { - return float4(float3(0.0,0.0,0.0),1.0); - } - - // If not, well, let''s put that GPU to good use! - float secs = floor(mod(time, 60.0)); - float mins = floor(mod(time, 3600.0)/60.0); - float hrs = floor(time/3600.0); - int h10 = int(floor(hrs/10.0)); - int h1 = int(floor(mod(hrs, 10.0))); - int m10 = int(floor(mins/10.0)); - int m1 = int(floor(mod(mins, 10.0))); - int s10 = int(floor(secs/10.0)); - int s1 = int(floor(mod(secs, 10.0))); - - float2 wspace = float2(6.5, 0.0); - float2 nspace = float2(3.5, 0.0); - float d = 1e10; - d = min(d, number(h10, p)); - d = min(d, number(h1, p-wspace)); - d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); - d = min(d, number(m10, p-2.0*wspace-nspace)); - d = min(d, number(m1, p-3.0*wspace-nspace)); - d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); - d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); - d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); - - float2 g; // For gradients returned from psrdnoise() - - // Digit outlines - float core = 1.0-aastep(0.2, d); - // "flare" is a wide blurry region around the characters, and - // "flarenoise" is a spatio-temporal modulation of its extents - // (slight flickering, but not all characters at the same time) - float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); - float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); - flare *= flare; // A more rapid decline towards the edge - // "glow" is a variation in the intensity of the glowy cathode (core) - float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); - // Now we mess up the distance field a little for the "halo" effect - d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); - d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); - // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube - // doesn''t have this, but it adds some appealing visual detail. - // Looks more like hot filaments than "cold cathodes", but... oh, well. - float halo = 1.0-smoothstep(-0.3, 0.3, d); - - // Brittle parameters! This scale/shift of p has a strong impact - // on the pattern at the edges of the grid through "anodefade". - float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); - float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders - // Fade the hexagonal holes in the anode towards the edges - float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), - max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); - float anode = 1.0 - aastep(0.1, anodedist - anodefade); - - float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); - float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know - - float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black - mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); - mixcolor = lerp(mixcolor, halocolor, 0.9*halo); - mixcolor = lerp(mixcolor, corecolor, core*glow); - mixcolor = lerp(mixcolor, anodecolorresult, anode); - mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box - return float4(mixcolor,1.0); -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10135,172 +7760,122 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorDepthShader { +function Get-OBSSceneItemId { -[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] +[Alias('obs.powershell.websocket.GetSceneItemId')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the colorDepth of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('colorDepth')] -[Single] -$ColorDepth, -# Set the pixelSize of OBSColorDepthShader -[ComponentModel.DefaultBindingProperty('pixelSize')] -[Single] -$PixelSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('searchOffset')] +[ValidateRange(-1,[int]::MaxValue)] +[double] +$SearchOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'color-depth' -$ShaderNoun = 'OBSColorDepthShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/tscfWM -uniform float colorDepth< - string label = "Color depth"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 100.0; - float step = 0.01; -> = 5.0; - -uniform float pixelSize< - string label = "Pixel Size"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 0.01; -> = 5.0; -float4 mainImage(VertData v_in) : TARGET -{ - // Change these to change results - float2 size = uv_size / pixelSize; - float2 uv = v_in.uv; - // Maps UV onto grid of variable size to pixilate the image - uv = round(uv*size)/size; - float4 col = image.Sample(textureSampler, uv); - // Maps color onto the specified color depth - return float4(round(col.r * colorDepth) / colorDepth, - round(col.g * colorDepth) / colorDepth, - round(col.b * colorDepth) / colorDepth, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10309,225 +7884,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSColorGradeFilterShader { +function Get-OBSSceneItemIndex { -[Alias('Set-OBSColorGradeFilterShader','Add-OBSColorGradeFilterShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] +[Alias('obs.powershell.websocket.GetSceneItemIndex')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the notes of OBSColorGradeFilterShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the lut of OBSColorGradeFilterShader -[ComponentModel.DefaultBindingProperty('lut')] -[String] -$Lut, -# Set the lut_amount_percent of OBSColorGradeFilterShader -[Alias('lut_amount_percent')] -[ComponentModel.DefaultBindingProperty('lut_amount_percent')] -[Int32] -$LutAmountPercent, -# Set the lut_scale_percent of OBSColorGradeFilterShader -[Alias('lut_scale_percent')] -[ComponentModel.DefaultBindingProperty('lut_scale_percent')] -[Int32] -$LutScalePercent, -# Set the lut_offset_percent of OBSColorGradeFilterShader -[Alias('lut_offset_percent')] -[ComponentModel.DefaultBindingProperty('lut_offset_percent')] -[Int32] -$LutOffsetPercent, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'color_grade_filter' -$ShaderNoun = 'OBSColorGradeFilterShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Grade Filter by Charles Fettinger for obs-shaderfilter plugin 4/2020 -//https://github.com/Oncorporation/obs-shaderfilter -//OBS messed up the LUT system, this is basically the old LUT system -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform string notes< - string widget_type = "info"; -> = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; -uniform texture2d lut< - string label = "LUT"; ->; -uniform int lut_amount_percent< - string label = "LUT amount percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_scale_percent< - string label = "LUT scale percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 100; -uniform int lut_offset_percent< - string label = "LUT offset percentage"; - string widget_type = "slider"; - int minimum = -200; - int maximum = 200; - int step = 1; -> = 0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); - float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); - float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float4 textureColor = image.Sample(textureSampler, v_in.uv); - float lumaLevel = textureColor.r * 0.2126 + textureColor.g * 0.7152 + textureColor.b * 0.0722; - float blueColor = float(lumaLevel);//textureColor.b * 63.0 + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } - float2 quad1; - quad1.y = floor(floor(float(blueColor)) / 8.0); - quad1.x = floor(float(blueColor)) - (quad1.y * 8.0); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 quad2; - quad2.y = floor(ceil(float(blueColor)) / 8.0); - quad2.x = ceil(float(blueColor)) - (quad2.y * 8.0); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - float2 texPos1; - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); +} - float2 texPos2; - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); - texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); - float4 newColor1 = lut.Sample(textureSampler, texPos1); - newColor1.rgb = newColor1.rgb * lut_scale + lut_offset; - float4 newColor2 = lut.Sample(textureSampler, texPos2); - newColor2.rgb = newColor2.rgb * lut_scale + lut_offset; - float4 luttedColor = lerp(newColor1, newColor2, frac(float(blueColor))); +} - float4 final_color = lerp(textureColor, luttedColor, lut_amount); - return float4(final_color.rgb, textureColor.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemLocked { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] +[Alias('obs.powershell.websocket.GetSceneItemLocked')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10536,358 +8122,236 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCornerPinShader { +function Get-OBSSceneItemSource { -[Alias('Set-OBSCornerPinShader','Add-OBSCornerPinShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] +[Alias('obs.powershell.websocket.GetSceneItemSource')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Antialias_Edges of OBSCornerPinShader -[Alias('Antialias_Edges')] -[ComponentModel.DefaultBindingProperty('Antialias_Edges')] -[Management.Automation.SwitchParameter] -$AntialiasEdges, -# Set the Top_Left_X of OBSCornerPinShader -[Alias('Top_Left_X')] -[ComponentModel.DefaultBindingProperty('Top_Left_X')] -[Single] -$TopLeftX, -# Set the Top_Left_Y of OBSCornerPinShader -[Alias('Top_Left_Y')] -[ComponentModel.DefaultBindingProperty('Top_Left_Y')] -[Single] -$TopLeftY, -# Set the Top_Right_X of OBSCornerPinShader -[Alias('Top_Right_X')] -[ComponentModel.DefaultBindingProperty('Top_Right_X')] -[Single] -$TopRightX, -# Set the Top_Right_Y of OBSCornerPinShader -[Alias('Top_Right_Y')] -[ComponentModel.DefaultBindingProperty('Top_Right_Y')] -[Single] -$TopRightY, -# Set the Bottom_Left_X of OBSCornerPinShader -[Alias('Bottom_Left_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] -[Single] -$BottomLeftX, -# Set the Bottom_Left_Y of OBSCornerPinShader -[Alias('Bottom_Left_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] -[Single] -$BottomLeftY, -# Set the Bottom_Right_X of OBSCornerPinShader -[Alias('Bottom_Right_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] -[Single] -$BottomRightX, -# Set the Bottom_Right_Y of OBSCornerPinShader -[Alias('Bottom_Right_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] -[Single] -$BottomRightY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'corner-pin' -$ShaderNoun = 'OBSCornerPinShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Corner Pin, Version 0.1, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Based on: -// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm -// https://www.youtube.com/c/InigoQuilez -// https://iquilezles.org/ -// and the derivative StreamFX Corner Pin effect by Xaymar -// https://github.com/Xaymar/obs-StreamFX -// -// Description: -// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily -// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying -// image with proper perspective without distortion. -// -// TODO: -// - Add feature to automatically quantize corners to pixel centers -// -// Changelog: -// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by -// Inigo Quilez that it was based upon. -uniform bool Antialias_Edges = true; -uniform float Top_Left_X< - string label = "Top left x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Left_Y< - string label = "Top left y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Top_Right_X< - string label = "Top right x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Top_Right_Y< - string label = "Top right y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_X< - string label = "Bottom left x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = -100.; -uniform float Bottom_Left_Y< - string label = "Bottom left y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_X< - string label = "Bottom right x"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; -uniform float Bottom_Right_Y< - string label = "Bottom right y"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.1; -> = 100.; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -// DEVELOPMENTAL DEBUGGING OPTIONS -//uniform float AAstrength = 1.0; -//uniform float AAdist = 1.0; -//uniform float debug_psmult = 1.0; -//#define PIXEL_SIZE_MULT 2.0 + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float cross2d(in float2 a, in float2 b) -{ - return (a.x * b.y) - (a.y * b.x); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) -{ - float2 result = float2(-1., -1.); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 e = b - a; - float2 f = d - a; - float2 g = a-b+c-d; - float2 h = p-a; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - 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); - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSceneItemTransform { - result = float2(u, v); - } - return result; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] +[Alias('obs.powershell.websocket.GetSceneItemTransform')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( -// distance to a line segment -float sdSegment( in float2 p, in float2 a, in float2 b ) -{ - p -= a; b -= a; - return length( p-b*saturate(dot(p,b)/dot(b,b)) ); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) -float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) -{ - //float ps = 2.0 * (2.0/uv_size.y); // Original -// float ps = debug_psmult * (2.0/uv_size.y); - float ps = (2.0/uv_size.y); -// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now - return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -float4 mainImage( VertData v_in ) : TARGET { - float2 p = 2.* v_in.uv - 1.; - - float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; - float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; - float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; - float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; - - // Convert from screen coords to potential Quad UV coordinates - float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { - return float4(0.0, 0.0, 0.0, 0.0); - } - float4 texel = image.Sample(textureSampler, uv); +process { - if ( Antialias_Edges ) { - // Anti-alias edges of texture - texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); - texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); - } - return texel; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -10896,185 +8360,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCrtCurvatureShader { +function Get-OBSSceneSceneTransitionOverride { -[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the strength of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# Set the border of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('border')] -[String] -$Border, -# Set the feathering of OBSCrtCurvatureShader -[ComponentModel.DefaultBindingProperty('feathering')] -[Single] -$Feathering, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'crt-curvature' -$ShaderNoun = 'OBSCrtCurvatureShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 200.; - float step = 0.01; -> = 33.33; - -uniform float4 border< - string label = "Border Color"; -> = {0., 0., 0., 1.}; - -uniform float feathering< - string label = "Feathering"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 33.33; - -float4 mainImage(VertData v_in) : TARGET -{ - float2 cc = v_in.uv - float2(0.5, 0.5); - float dist = dot(cc, cc) * strength / 100.0; - float2 bent = v_in.uv + cc * (1.0 + dist) * dist; - if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { - return border; - } - if (feathering >= .01) { - float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; - float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); - borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; - float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); - return lerp(border, image.Sample(textureSampler, bent), borderFade); - } - return image.Sample(textureSampler, bent); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11083,234 +8473,214 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCubeRotatingShader { +function Get-OBSSceneTransition { -[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] +[Alias('obs.powershell.websocket.GetSceneTransitionList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the images of OBSCubeRotatingShader -[ComponentModel.DefaultBindingProperty('images')] -[Int32] -$Images, -# Set the speed of OBSCubeRotatingShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the shadow of OBSCubeRotatingShader -[ComponentModel.DefaultBindingProperty('shadow')] -[Single] -$Shadow, -# Set the other_image1 of OBSCubeRotatingShader -[Alias('other_image1')] -[ComponentModel.DefaultBindingProperty('other_image1')] -[String] -$OtherImage1, -# Set the other_image2 of OBSCubeRotatingShader -[Alias('other_image2')] -[ComponentModel.DefaultBindingProperty('other_image2')] -[String] -$OtherImage2, -# Set the other_image3 of OBSCubeRotatingShader -[Alias('other_image3')] -[ComponentModel.DefaultBindingProperty('other_image3')] -[String] -$OtherImage3, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cube_rotating' -$ShaderNoun = 'OBSCubeRotatingShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int images< - string label = "Images"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "1"; - int option_1_value = 2; - string option_1_label = "2"; - int option_2_value = 4; - string option_2_label = "4"; -> = 1; - -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 0.5; -uniform float shadow< - string label = "Shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.5; - float step = 0.001; -> = 1.0; -uniform texture2d other_image1; -uniform texture2d other_image2; -uniform texture2d other_image3; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -#define PI 3.14159265359 -float4 mainImage(VertData v_in) : TARGET -{ - float t = elapsed_time * speed; - float4 c = float4(0,0,0,0); - for(float side = 0.0; side<4.0; side += 1.0){ - float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; - float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; - if(left < right){ - float2 uv; - uv.x = (v_in.uv.x-left)/(right-left); - float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; - float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; - float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); - uv.y = (v_in.uv.y-0.5)*size+0.5; - float4 sample = float4(0,0,0,0); - if(images <= 1 || side == 0.0){ - sample = image.Sample(textureSampler, uv); - }else if(images == 2){ - if(side == 1.0 || side == 3.0){ - sample = other_image1.Sample(textureSampler, uv); - }else{ - sample = image.Sample(textureSampler, uv); - } - }else if(images == 4){ - if(side == 1.0){ - sample = other_image1.Sample(textureSampler, uv); - }else if(side == 2.0){ - sample = other_image2.Sample(textureSampler, uv); - }else if(side == 3.0){ - sample = other_image3.Sample(textureSampler, uv); - }else{ - sample = image.Sample(textureSampler, uv); - } - } - if(sample.a > 0.0){ - c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); - } - } - } - return c; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceActive { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] +[Alias('obs.powershell.websocket.GetSourceActive')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11319,187 +8689,224 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurveShader { +function Get-OBSSourceFilter { -[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] +[Alias('obs.powershell.websocket.GetSourceFilter')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the strength of OBSCurveShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, -# Set the scale of OBSCurveShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, -# Set the curve_color of OBSCurveShader -[Alias('curve_color')] -[ComponentModel.DefaultBindingProperty('curve_color')] -[String] -$CurveColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'curve' -$ShaderNoun = 'OBSCurveShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 -uniform float strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float4 curve_color = {0,0,0,0}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - const float ydiff = 1.0; - uv -= float2(0.5,ydiff); - uv.x *= ( uv_size.x / uv_size.y); - uv /= scale; - if(strength > 0.0){ - float d = tan((1.0-strength)*PI/2.0); - float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); - float2 center = float2(0.0,(1.0-ydiff)+d); - float pd = distance(uv, center); - if(pd < r) - return curve_color; - float angle = atan2(center.x-uv.x,center.y-uv.y); - uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); - } - uv.x /= ( uv_size.x / uv_size.y); - uv += float2(0.5,ydiff); - return image.Sample(textureSampler,uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSSourceFilterDefaultSettings { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] +[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterKind')] +[string] +$FilterKind, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11508,354 +8915,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCutRectPerCornerShader { +function Get-OBSSourceFilterKind { -[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] +[Alias('obs.powershell.websocket.GetSourceFilterKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the corner_tl of OBSCutRectPerCornerShader -[Alias('corner_tl')] -[ComponentModel.DefaultBindingProperty('corner_tl')] -[Int32] -$CornerTl, -# Set the corner_tr of OBSCutRectPerCornerShader -[Alias('corner_tr')] -[ComponentModel.DefaultBindingProperty('corner_tr')] -[Int32] -$CornerTr, -# Set the corner_br of OBSCutRectPerCornerShader -[Alias('corner_br')] -[ComponentModel.DefaultBindingProperty('corner_br')] -[Int32] -$CornerBr, -# Set the corner_bl of OBSCutRectPerCornerShader -[Alias('corner_bl')] -[ComponentModel.DefaultBindingProperty('corner_bl')] -[Int32] -$CornerBl, -# Set the border_thickness of OBSCutRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSCutRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] -[String] -$BorderColor, -# Set the border_alpha_start of OBSCutRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSCutRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSCutRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] -[Single] -$AlphaCutOff, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cut_rect_per_corner' -$ShaderNoun = 'OBSCutRectPerCornerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_tl< - string label = "Corner top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_tr< - string label = "Corner top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_br< - string label = "Corner bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int corner_bl< - string label = "Corner bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border aplha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border aplha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; - int corner_right = corner_tr>corner_br?corner_tr:corner_br; - int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; - int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = closedEdgeXabs+closedEdgeYabs; - if(d>corner_radius){ - if(d-corner_radius <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - return float4(0.0,0.0,0.0,0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -11864,192 +9018,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCylinderShader { +function Get-OBSSourceFilterList { -[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] +[Alias('obs.powershell.websocket.GetSourceFilterList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the cylinder_factor of OBSCylinderShader -[Alias('cylinder_factor')] -[ComponentModel.DefaultBindingProperty('cylinder_factor')] -[Single] -$CylinderFactor, -# Set the background_cut of OBSCylinderShader -[Alias('background_cut')] -[ComponentModel.DefaultBindingProperty('background_cut')] -[Single] -$BackgroundCut, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'cylinder' -$ShaderNoun = 'OBSCylinderShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float cylinder_factor< - string label = "Cylinder factor"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.2; -uniform float background_cut< - string label = "Background cut"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.1; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.x -= 0.5; - float bend = sqrt(1.0 - uv.x*uv.x*4); - uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - uv.x += 0.5; - float4 front_color = image.Sample(textureSampler, uv); - front_color.rgb *= bend/2+0.5; - if(front_color.a >= 1.0) - return front_color; - - uv = v_in.uv; - uv.x -= 0.5; - if(abs(uv.x) < background_cut) - return front_color; - uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; - uv.y-=cylinder_factor/2; - uv.x /= 2; - if(uv.x > 0){ - uv.x = 1.0 - uv.x; - }else{ - uv.x = 0 - uv.x; - } - float4 back_color = image.Sample(textureSampler, uv); - back_color.rgb *= 0.5-bend/2; - front_color.rgb *= front_color.a; - front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; - front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; - return front_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12058,161 +9131,134 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDarkenShader { +function Get-OBSSourceScreenshot { -[Alias('Set-OBSDarkenShader','Add-OBSDarkenShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] +[Alias('obs.powershell.websocket.GetSourceScreenshot')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Opacity_Percentage of OBSDarkenShader -[Alias('Opacity_Percentage')] -[ComponentModel.DefaultBindingProperty('Opacity_Percentage')] -[Single] -$OpacityPercentage, -# Set the Fill_Percentage of OBSDarkenShader -[Alias('Fill_Percentage')] -[ComponentModel.DefaultBindingProperty('Fill_Percentage')] -[Single] -$FillPercentage, -# Set the Notes of OBSDarkenShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'darken' -$ShaderNoun = 'OBSDarkenShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Opacity_Percentage = 100.0; -uniform float Fill_Percentage = 100.0; -uniform string Notes = "Simulates a photo editing darken layer blending mode. Fill percentage is the interior alpha and Opacity is the layer alpha."; -float4 mainImage(VertData v_in) : TARGET -{ - float4 other = float4(1.0, 1.0, 1.0, 1.0); - float4 base = image.Sample(textureSampler, v_in.uv); - float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); - float4 gray = float4(luminance,luminance,luminance, 1.0); - return min(base,other); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12221,198 +9267,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDeadPixelFixerShader { +function Get-OBSSpecialInputs { -[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] +[Alias('obs.powershell.websocket.GetSpecialInputs')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Dead_Pixel_X of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_X')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] -[Int32] -$DeadPixelX, -# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader -[Alias('Dead_Pixel_Y')] -[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] -[Int32] -$DeadPixelY, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'dead-pixel-fixer' -$ShaderNoun = 'OBSDeadPixelFixerShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. -// The pixel located at the user configured offset will have its color overridden by taking the average of the -// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. -// -// Changelog: -// 0.01 - Initial release - -uniform int Dead_Pixel_X< - string label = "Dead Pixel X"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 2000; - int step = 1; -> = 0; -uniform int Dead_Pixel_Y< - string label = "Dead Pixel Y"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 2000; - int step = 1; -> = 0; -float3 blur_dead_pixel(in float2 pos) -{ - float3 color; - color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; - color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; - return color * 0.25; -} -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float2 pos = v_in.pos.xy; - float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); - float4 obstex = image.Sample(textureSampler, uv); - float3 color; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { - color = blur_dead_pixel(pos); - } else { - color.rgb = obstex.rgb; - } - return float4( color, obstex.a); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStats { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStats')] +[Alias('obs.powershell.websocket.GetStats')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12421,488 +9473,307 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDensitySatHueShader { +function Get-OBSStreamServiceSettings { -[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] +[Alias('obs.powershell.websocket.GetStreamServiceSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the notes of OBSDensitySatHueShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the density_r of OBSDensitySatHueShader -[Alias('density_r')] -[ComponentModel.DefaultBindingProperty('density_r')] -[Single] -$DensityR, -# Set the saturation_r of OBSDensitySatHueShader -[Alias('saturation_r')] -[ComponentModel.DefaultBindingProperty('saturation_r')] -[Single] -$SaturationR, -# Set the hueShift_r of OBSDensitySatHueShader -[Alias('hueShift_r')] -[ComponentModel.DefaultBindingProperty('hueShift_r')] -[Single] -$HueShiftR, -# Set the density_y of OBSDensitySatHueShader -[Alias('density_y')] -[ComponentModel.DefaultBindingProperty('density_y')] -[Single] -$DensityY, -# Set the saturation_y of OBSDensitySatHueShader -[Alias('saturation_y')] -[ComponentModel.DefaultBindingProperty('saturation_y')] -[Single] -$SaturationY, -# Set the hueShift_y of OBSDensitySatHueShader -[Alias('hueShift_y')] -[ComponentModel.DefaultBindingProperty('hueShift_y')] -[Single] -$HueShiftY, -# Set the density_g of OBSDensitySatHueShader -[Alias('density_g')] -[ComponentModel.DefaultBindingProperty('density_g')] -[Single] -$DensityG, -# Set the saturation_g of OBSDensitySatHueShader -[Alias('saturation_g')] -[ComponentModel.DefaultBindingProperty('saturation_g')] -[Single] -$SaturationG, -# Set the hueShift_g of OBSDensitySatHueShader -[Alias('hueShift_g')] -[ComponentModel.DefaultBindingProperty('hueShift_g')] -[Single] -$HueShiftG, -# Set the density_c of OBSDensitySatHueShader -[Alias('density_c')] -[ComponentModel.DefaultBindingProperty('density_c')] -[Single] -$DensityC, -# Set the saturation_c of OBSDensitySatHueShader -[Alias('saturation_c')] -[ComponentModel.DefaultBindingProperty('saturation_c')] -[Single] -$SaturationC, -# Set the hueShift_c of OBSDensitySatHueShader -[Alias('hueShift_c')] -[ComponentModel.DefaultBindingProperty('hueShift_c')] -[Single] -$HueShiftC, -# Set the density_b of OBSDensitySatHueShader -[Alias('density_b')] -[ComponentModel.DefaultBindingProperty('density_b')] -[Single] -$DensityB, -# Set the saturation_b of OBSDensitySatHueShader -[Alias('saturation_b')] -[ComponentModel.DefaultBindingProperty('saturation_b')] -[Single] -$SaturationB, -# Set the hueShift_b of OBSDensitySatHueShader -[Alias('hueShift_b')] -[ComponentModel.DefaultBindingProperty('hueShift_b')] -[Single] -$HueShiftB, -# Set the density_m of OBSDensitySatHueShader -[Alias('density_m')] -[ComponentModel.DefaultBindingProperty('density_m')] -[Single] -$DensityM, -# Set the saturation_m of OBSDensitySatHueShader -[Alias('saturation_m')] -[ComponentModel.DefaultBindingProperty('saturation_m')] -[Single] -$SaturationM, -# Set the hueShift_m of OBSDensitySatHueShader -[Alias('hueShift_m')] -[ComponentModel.DefaultBindingProperty('hueShift_m')] -[Single] -$HueShiftM, -# Set the global_density of OBSDensitySatHueShader -[Alias('global_density')] -[ComponentModel.DefaultBindingProperty('global_density')] -[Single] -$GlobalDensity, -# Set the global_saturation of OBSDensitySatHueShader -[Alias('global_saturation')] -[ComponentModel.DefaultBindingProperty('global_saturation')] -[Single] -$GlobalSaturation, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'density_sat_hue' -$ShaderNoun = 'OBSDensitySatHueShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Density-Saturation-Hue Shader for OBS Shaderfilter -// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 - -uniform string notes< - string widget_type = "info"; -> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; - -uniform float density_r < - string label = "Red Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float saturation_r < - string label = "Red Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float hueShift_r < - string label = "Red Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float density_y < - string label = "Yellow Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float saturation_y < - string label = "Yellow Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float hueShift_y < - string label = "Yellow Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float density_g < - string label = "Green Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float saturation_g < - string label = "Green Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform float hueShift_g < - string label = "Green Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +} -uniform float density_c < - string label = "Cyan Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float saturation_c < - string label = "Cyan Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +} -uniform float hueShift_c < - string label = "Cyan Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStreamStatus { -uniform float density_b < - string label = "Blue Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float saturation_b < - string label = "Blue Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] +[Alias('obs.powershell.websocket.GetStreamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform float hueShift_b < - string label = "Blue Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float density_m < - string label = "Magenta Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +process { -uniform float saturation_m < - string label = "Magenta Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float hueShift_m < - string label = "Magenta Hue Shift"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float global_density < - string label = "Global Density"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float global_saturation < - string label = "Global Sat"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -// Tetrahedral interpolation based on the ordering of the input color channels -float3 applyAdjustments(float3 p) { - // Pre-calculate the flipped density values for each channel: - float d_r = -(density_r + global_density); - float d_y = -(density_y + global_density); - float d_g = -(density_g + global_density); - float d_c = -(density_c + global_density); - float d_b = -(density_b + global_density); - float d_m = -(density_m + global_density); - - // Compute the color vectors for each hue region using the flipped densities: - float3 red = float3( - 1.0 + d_r, - d_r - (saturation_r + global_saturation), - d_r + hueShift_r - (saturation_r + global_saturation) - ); - - float3 yellow = float3( - 1.0 + hueShift_y + d_y, - 1.0 + d_y, - d_y - (saturation_y + global_saturation) - ); - - float3 green = float3( - d_g - (saturation_g + global_saturation), - 1.0 + d_g, - d_g + hueShift_g - (saturation_g + global_saturation) - ); - - float3 cyan = float3( - d_c - (saturation_c + global_saturation), - 1.0 + hueShift_c + d_c, - 1.0 + d_c - ); - - float3 blue = float3( - d_b + hueShift_b - (saturation_b + global_saturation), - d_b - (saturation_b + global_saturation), - 1.0 + d_b - ); - - float3 magenta = float3( - 1.0 + d_m, - d_m - (saturation_m + global_saturation), - 1.0 + hueShift_m + d_m - ); - - // Define the black and white endpoints: - float3 blk = float3(0.0, 0.0, 0.0); - float3 wht = float3(1.0, 1.0, 1.0); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - float3 rgb; - if (p.r > p.g) { - if (p.g > p.b) { - // p.r >= p.g >= p.b - rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); - } else if (p.r > p.b) { - // p.r >= p.b > p.g - rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); - } else { - // p.b >= p.r > p.g - rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - } else { - if (p.b > p.g) { - // p.b >= p.g >= p.r - rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; - } else if (p.b > p.r) { - // p.g >= p.r and p.b > p.r - rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - // p.g >= p.b >= p.r - rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } - return clamp(rgb, 0.0, 1.0); -} -float4 mainImage(VertData v_in) : TARGET { - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float3 adjustedColor = applyAdjustments(inputColor.rgb); - return float4(adjustedColor, inputColor.a); } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +} - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSStudioModeEnabled { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] +[Alias('obs.powershell.websocket.GetStudioModeEnabled')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -12911,242 +9782,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDiffuseTransitionShader { +function Get-OBSTransitionKind { -[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetTransitionKindList')] +[Alias('obs.powershell.websocket.GetTransitionKindList')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the image_a of OBSDiffuseTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSDiffuseTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSDiffuseTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSDiffuseTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the num_samples of OBSDiffuseTransitionShader -[Alias('num_samples')] -[ComponentModel.DefaultBindingProperty('num_samples')] -[Int32] -$NumSamples, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'diffuse_transition' -$ShaderNoun = 'OBSDiffuseTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/4cK3z1 -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -// Diffuse (move pixels) between two 2D images -// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) -// Note: the approach in this demo is different - rather than randomising paths we use means -// increase for greater precision - this is O(n^2) :( -uniform int num_samples< - string label = "Number of samples (10)"; - string widget_type = "slider"; - int minimum = 2; - int maximum = 100; - int step = 1; -> = 10; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (transition_time < 0.00001) { - return image_a.Sample(textureSampler, uv); - } - - // we need to normalise the distributions so just sum the samples for a division later - // note: could calculate this once per image in a buffer or something - float3 from_total = float3(0.0,0.0,0.0); - float3 to_total = float3(0.0,0.0,0.0); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - for (int i=0; iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13155,232 +9988,204 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDigitalRainShader { +function Get-OBSVideoSettings { -[Alias('Set-OBSDigitalRainShader','Add-OBSDigitalRainShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] +[Alias('obs.powershell.websocket.GetVideoSettings')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the font of OBSDigitalRainShader -[ComponentModel.DefaultBindingProperty('font')] -[String] -$Font, -# Set the noise of OBSDigitalRainShader -[ComponentModel.DefaultBindingProperty('noise')] -[String] -$Noise, -# Set the base_color of OBSDigitalRainShader -[Alias('base_color')] -[ComponentModel.DefaultBindingProperty('base_color')] -[String] -$BaseColor, -# Set the rain_speed of OBSDigitalRainShader -[Alias('rain_speed')] -[ComponentModel.DefaultBindingProperty('rain_speed')] -[Single] -$RainSpeed, -# Set the char_speed of OBSDigitalRainShader -[Alias('char_speed')] -[ComponentModel.DefaultBindingProperty('char_speed')] -[Single] -$CharSpeed, -# Set the glow_contrast of OBSDigitalRainShader -[Alias('glow_contrast')] -[ComponentModel.DefaultBindingProperty('glow_contrast')] -[Single] -$GlowContrast, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'digital-rain' -$ShaderNoun = 'OBSDigitalRainShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/ldccW4 by WillKirkby -#ifndef OPENGL -#define fract frac -#define mix lerp -#endif + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform texture2d font = "font.png"; -uniform texture2d noise = "noise.png"; -uniform float4 base_color = {.1, 1.0, .35, 1.0}; -uniform float rain_speed< - string label = "Rain Speed"; - string widget_type = "slider"; - float minimum = 0.001; - float maximum = 2.0; - float step = .001; -> = 1.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float char_speed< - string label = "Character Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = .001; -> = 1.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float glow_contrast< - string label = "Glow contrast"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.5; - float step = .001; -> = 1.0; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float mod(float x, float y) -{ - return x - y * floor(x/y); } -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x/y); -} -float text(float2 fragCoord) -{ - float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; - float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; - uv = uv*.8+.1; // scale the letters up a bit - block += elapsed_time*.002*char_speed; - uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters - uv *= .0625; // bring back into 0-1 range - return font.Sample(textureSampler, uv).r; -} +} -float3 rain(float2 fragCoord) -{ - fragCoord.x -= mod(fragCoord.x, 16.); - float offset=sin(fragCoord.x*15.); - float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; - - float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); - return base_color.rgb / pow(y*20.0, glow_contrast); -} + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSVirtualCamStatus { -float4 mainImage(VertData v_in) : TARGET -{ - return mix(image.Sample(textureSampler, v_in.uv),float4(rain(float2(v_in.uv.x,1.0-v_in.uv.y)*uv_size),1.0), text(v_in.uv*uv_size)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] +[Alias('obs.powershell.websocket.GetVirtualCamStatus')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13389,433 +10194,334 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDisplacementMapAdvancedInvertShader { +function Open-OBSInputFiltersDialog { -[Alias('Set-OBSDisplacementMapAdvancedInvertShader','Add-OBSDisplacementMapAdvancedInvertShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] +[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] param( -# Set the displacement_info of OBSDisplacementMapAdvancedInvertShader -[Alias('displacement_info')] -[ComponentModel.DefaultBindingProperty('displacement_info')] -[String] -$DisplacementInfo, -# Set the displacement_x of OBSDisplacementMapAdvancedInvertShader -[Alias('displacement_x')] -[ComponentModel.DefaultBindingProperty('displacement_x')] -[Single] -$DisplacementX, -# Set the displacement_y of OBSDisplacementMapAdvancedInvertShader -[Alias('displacement_y')] -[ComponentModel.DefaultBindingProperty('displacement_y')] -[Single] -$DisplacementY, -# Set the displacement_curve of OBSDisplacementMapAdvancedInvertShader -[Alias('displacement_curve')] -[ComponentModel.DefaultBindingProperty('displacement_curve')] -[Int32] -$DisplacementCurve, -# Set the blur_info of OBSDisplacementMapAdvancedInvertShader -[Alias('blur_info')] -[ComponentModel.DefaultBindingProperty('blur_info')] -[String] -$BlurInfo, -# Set the blur_size of OBSDisplacementMapAdvancedInvertShader -[Alias('blur_size')] -[ComponentModel.DefaultBindingProperty('blur_size')] -[Single] -$BlurSize, -# Set the blur_quality of OBSDisplacementMapAdvancedInvertShader -[Alias('blur_quality')] -[ComponentModel.DefaultBindingProperty('blur_quality')] -[Single] -$BlurQuality, -# Set the blur_directions of OBSDisplacementMapAdvancedInvertShader -[Alias('blur_directions')] -[ComponentModel.DefaultBindingProperty('blur_directions')] -[Single] -$BlurDirections, -# Set the blur_angle of OBSDisplacementMapAdvancedInvertShader -[Alias('blur_angle')] -[ComponentModel.DefaultBindingProperty('blur_angle')] -[Single] -$BlurAngle, -# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedInvertShader -[Alias('chromatic_aberration_info')] -[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] -[String] -$ChromaticAberrationInfo, -# Set the chromatic_aberration of OBSDisplacementMapAdvancedInvertShader -[Alias('chromatic_aberration')] -[ComponentModel.DefaultBindingProperty('chromatic_aberration')] -[Single] -$ChromaticAberration, -# Set the colorize_info of OBSDisplacementMapAdvancedInvertShader -[Alias('colorize_info')] -[ComponentModel.DefaultBindingProperty('colorize_info')] -[String] -$ColorizeInfo, -# Set the colorize_color of OBSDisplacementMapAdvancedInvertShader -[Alias('colorize_color')] -[ComponentModel.DefaultBindingProperty('colorize_color')] -[String] -$ColorizeColor, -# Set the flags_info of OBSDisplacementMapAdvancedInvertShader -[Alias('flags_info')] -[ComponentModel.DefaultBindingProperty('flags_info')] -[String] -$FlagsInfo, -# Set the blue_affects_strength of OBSDisplacementMapAdvancedInvertShader -[Alias('blue_affects_strength')] -[ComponentModel.DefaultBindingProperty('blue_affects_strength')] -[Management.Automation.SwitchParameter] -$BlueAffectsStrength, -# Set the blue_affects_colorize of OBSDisplacementMapAdvancedInvertShader -[Alias('blue_affects_colorize')] -[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] -[Management.Automation.SwitchParameter] -$BlueAffectsColorize, -# Set the blue_affects_blur of OBSDisplacementMapAdvancedInvertShader -[Alias('blue_affects_blur')] -[ComponentModel.DefaultBindingProperty('blue_affects_blur')] -[Management.Automation.SwitchParameter] -$BlueAffectsBlur, -# Set the alpha_affects_strength of OBSDisplacementMapAdvancedInvertShader -[Alias('alpha_affects_strength')] -[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] -[Management.Automation.SwitchParameter] -$AlphaAffectsStrength, -# Set the apply_alpha of OBSDisplacementMapAdvancedInvertShader -[Alias('apply_alpha')] -[ComponentModel.DefaultBindingProperty('apply_alpha')] -[Management.Automation.SwitchParameter] -$ApplyAlpha, -# Set the background_layer of OBSDisplacementMapAdvancedInvertShader -[Alias('background_layer')] -[ComponentModel.DefaultBindingProperty('background_layer')] -[String] -$BackgroundLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'displacement_map_advanced_invert' -$ShaderNoun = 'OBSDisplacementMapAdvancedInvertShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform string displacement_info< - string label = "Displacement"; - string widget_type = "info"; -> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; -uniform float displacement_x< - string label = "Displacement X (px)"; -> = 16.0; -uniform float displacement_y< - string label = "Displacement Y (px)"; -> = 16.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int displacement_curve< - string label = "Displacement Curve"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "Linear"; - int option_1_value = 2; - string option_1_label = "Quadratic"; - int option_2_value = 3; - string option_2_label = "Cubic"; -> = 0; - -uniform string blur_info< - string label = "Blur"; - string widget_type = "info"; -> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float blur_size< - string label = "Blur Size (px)"; -> = 8.0; // BLUR SIZE (Radius) -uniform float blur_quality< - string label = "Blur Quality (4.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 16; - float step = 1.0; -> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) -uniform float blur_directions< - string label = "Blur Directions (16.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 24; - float step = 1.0; -> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) -uniform float blur_angle< - string label = "Blur Angle (degrees)"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 360; - float step = 1.0; -> = 0; // BLUR Angle (starting angle of first sample) + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform string chromatic_aberration_info< - string label = "Chromatic Aberration"; - string widget_type = "info"; -> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float chromatic_aberration< - string label = "Chromatic Aberration (0.0)"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform string colorize_info< - string label = "Color"; - string widget_type = "info"; -> = "Imitates how light change color (tinted glass for instance)"; +} -uniform float4 colorize_color< - string label = "Colorize"; -> = {0.0, 0.0, 0.0, 0.0}; -uniform string flags_info< - string label = "Additional Channels"; - string widget_type = "info"; -> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; +} -uniform bool blue_affects_strength< - string label = "Blue channel affects displacement"; -> = false; -uniform bool blue_affects_colorize< - string label = "Blue channel affects colorize"; -> = false; -uniform bool blue_affects_blur< - string label = "Blue channel affects blur"; -> = false; -uniform bool alpha_affects_strength< - string label = "Alpha channel affects displacement"; -> = false; -uniform bool apply_alpha< - string label = "Apply alpha"; -> = false; + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSInputInteractDialog { -uniform texture2d background_layer < - string label = "Background Layer"; ->; -float4 mainImage(VertData v_in) : TARGET -{ - float Pi = 6.28318530718; // Pi*2 - float blurAngleRadians = (blur_angle / 360.) * Pi; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] +[Alias('obs.powershell.websocket.OpenInputInteractDialog')] +param( - float4 map_rgba = image.Sample(textureSampler, v_in.uv); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; - float4 displace = float4( - (map_rgba.r * 2) - 1, - (map_rgba.g * 2) - 1, - (map_rgba.b * 2) - 1, - map_rgba.a - ); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float2 displace_uv = float2(displace.r, displace.g) * displace_strength; - for(int p=1; p 0 && displace.a > 0) { - float4 oc = base_rgba; - float transparent = oc.a; - int count = 1; - float samples = oc.a; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - // Blur calculations - [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { - [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { - float size = blur_size; - float4 sc; - - if (blue_affects_blur) { - size *= displace.b; + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (chromatic_aberration) { - float4 sc_r = background_layer.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - float4 sc_g = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - float4 sc_b = background_layer.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - - sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - sc = background_layer.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - transparent += sc.a; - count++; - base_rgba += sc * sc.a; - samples += sc.a; - } - } +} - //Calculate averages - if (samples > 0.0) - base_rgba /= samples; - base_rgba.a = transparent / count; - } +} - if (blue_affects_colorize) { - base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); - } else { - base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); - } + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSInputPropertiesDialog { - if (apply_alpha) { - float4 background_rgba = background_layer.Sample(textureSampler, v_in.uv); - return lerp( - float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), - float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), - displace.a - ); - } - - return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] +[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] +param( - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -13824,433 +10530,349 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDisplacementMapAdvancedShader { +function Open-OBSSourceProjector { -[Alias('Set-OBSDisplacementMapAdvancedShader','Add-OBSDisplacementMapAdvancedShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] +[Alias('obs.powershell.websocket.OpenSourceProjector')] param( -# Set the displacement_info of OBSDisplacementMapAdvancedShader -[Alias('displacement_info')] -[ComponentModel.DefaultBindingProperty('displacement_info')] -[String] -$DisplacementInfo, -# Set the displacement_x of OBSDisplacementMapAdvancedShader -[Alias('displacement_x')] -[ComponentModel.DefaultBindingProperty('displacement_x')] -[Single] -$DisplacementX, -# Set the displacement_y of OBSDisplacementMapAdvancedShader -[Alias('displacement_y')] -[ComponentModel.DefaultBindingProperty('displacement_y')] -[Single] -$DisplacementY, -# Set the displacement_curve of OBSDisplacementMapAdvancedShader -[Alias('displacement_curve')] -[ComponentModel.DefaultBindingProperty('displacement_curve')] -[Int32] -$DisplacementCurve, -# Set the blur_info of OBSDisplacementMapAdvancedShader -[Alias('blur_info')] -[ComponentModel.DefaultBindingProperty('blur_info')] -[String] -$BlurInfo, -# Set the blur_size of OBSDisplacementMapAdvancedShader -[Alias('blur_size')] -[ComponentModel.DefaultBindingProperty('blur_size')] -[Single] -$BlurSize, -# Set the blur_quality of OBSDisplacementMapAdvancedShader -[Alias('blur_quality')] -[ComponentModel.DefaultBindingProperty('blur_quality')] -[Single] -$BlurQuality, -# Set the blur_directions of OBSDisplacementMapAdvancedShader -[Alias('blur_directions')] -[ComponentModel.DefaultBindingProperty('blur_directions')] -[Single] -$BlurDirections, -# Set the blur_angle of OBSDisplacementMapAdvancedShader -[Alias('blur_angle')] -[ComponentModel.DefaultBindingProperty('blur_angle')] -[Single] -$BlurAngle, -# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedShader -[Alias('chromatic_aberration_info')] -[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] -[String] -$ChromaticAberrationInfo, -# Set the chromatic_aberration of OBSDisplacementMapAdvancedShader -[Alias('chromatic_aberration')] -[ComponentModel.DefaultBindingProperty('chromatic_aberration')] -[Single] -$ChromaticAberration, -# Set the colorize_info of OBSDisplacementMapAdvancedShader -[Alias('colorize_info')] -[ComponentModel.DefaultBindingProperty('colorize_info')] -[String] -$ColorizeInfo, -# Set the colorize_color of OBSDisplacementMapAdvancedShader -[Alias('colorize_color')] -[ComponentModel.DefaultBindingProperty('colorize_color')] -[String] -$ColorizeColor, -# Set the flags_info of OBSDisplacementMapAdvancedShader -[Alias('flags_info')] -[ComponentModel.DefaultBindingProperty('flags_info')] -[String] -$FlagsInfo, -# Set the blue_affects_strength of OBSDisplacementMapAdvancedShader -[Alias('blue_affects_strength')] -[ComponentModel.DefaultBindingProperty('blue_affects_strength')] -[Management.Automation.SwitchParameter] -$BlueAffectsStrength, -# Set the blue_affects_colorize of OBSDisplacementMapAdvancedShader -[Alias('blue_affects_colorize')] -[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] -[Management.Automation.SwitchParameter] -$BlueAffectsColorize, -# Set the blue_affects_blur of OBSDisplacementMapAdvancedShader -[Alias('blue_affects_blur')] -[ComponentModel.DefaultBindingProperty('blue_affects_blur')] -[Management.Automation.SwitchParameter] -$BlueAffectsBlur, -# Set the alpha_affects_strength of OBSDisplacementMapAdvancedShader -[Alias('alpha_affects_strength')] -[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] -[Management.Automation.SwitchParameter] -$AlphaAffectsStrength, -# Set the apply_alpha of OBSDisplacementMapAdvancedShader -[Alias('apply_alpha')] -[ComponentModel.DefaultBindingProperty('apply_alpha')] -[Management.Automation.SwitchParameter] -$ApplyAlpha, -# Set the mask_layer of OBSDisplacementMapAdvancedShader -[Alias('mask_layer')] -[ComponentModel.DefaultBindingProperty('mask_layer')] -[String] -$MaskLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'displacement_map_advanced' -$ShaderNoun = 'OBSDisplacementMapAdvancedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform string displacement_info< - string label = "Displacement"; - string widget_type = "info"; -> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; - -uniform float displacement_x< - string label = "Displacement X (px)"; -> = 16.0; -uniform float displacement_y< - string label = "Displacement Y (px)"; -> = 16.0; -uniform int displacement_curve< - string label = "Displacement Curve"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "Linear"; - int option_1_value = 2; - string option_1_label = "Quadratic"; - int option_2_value = 3; - string option_2_label = "Cubic"; -> = 0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform string blur_info< - string label = "Blur"; - string widget_type = "info"; -> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float blur_size< - string label = "Blur Size (px)"; -> = 8.0; // BLUR SIZE (Radius) -uniform float blur_quality< - string label = "Blur Quality (4.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 16; - float step = 1.0; -> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) -uniform float blur_directions< - string label = "Blur Directions (16.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 24; - float step = 1.0; -> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) -uniform float blur_angle< - string label = "Blur Angle (degrees)"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 360; - float step = 1.0; -> = 0; // BLUR Angle (starting angle of first sample) + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform string chromatic_aberration_info< - string label = "Chromatic Aberration"; - string widget_type = "info"; -> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform float chromatic_aberration< - string label = "Chromatic Aberration (0.0)"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -uniform string colorize_info< - string label = "Color"; - string widget_type = "info"; -> = "Imitates how light change color (tinted glass for instance)"; +} -uniform float4 colorize_color< - string label = "Colorize"; -> = {0.0, 0.0, 0.0, 0.0}; -uniform string flags_info< - string label = "Additional Channels"; - string widget_type = "info"; -> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; +} -uniform bool blue_affects_strength< - string label = "Blue channel affects displacement"; -> = false; -uniform bool blue_affects_colorize< - string label = "Blue channel affects colorize"; -> = false; -uniform bool blue_affects_blur< - string label = "Blue channel affects blur"; -> = false; -uniform bool alpha_affects_strength< - string label = "Alpha channel affects displacement"; -> = false; -uniform bool apply_alpha< - string label = "Apply alpha"; -> = false; + +#.ExternalHelp obs-powershell-Help.xml +function Open-OBSVideoMixProjector { -uniform texture2d mask_layer < - string label = "Mask Layer"; ->; -float4 mainImage(VertData v_in) : TARGET -{ - float Pi = 6.28318530718; // Pi*2 - float blurAngleRadians = (blur_angle / 360.) * Pi; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] +[Alias('obs.powershell.websocket.OpenVideoMixProjector')] +param( - float4 map_rgba = mask_layer.Sample(textureSampler, v_in.uv); +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('videoMixType')] +[string] +$VideoMixType, - float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; - float4 displace = float4( - (map_rgba.r * 2) - 1, - (map_rgba.g * 2) - 1, - (map_rgba.b * 2) - 1, - map_rgba.a - ); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorIndex')] +[double] +$MonitorIndex, - float2 displace_uv = float2(displace.r, displace.g) * displace_strength; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('projectorGeometry')] +[string] +$ProjectorGeometry, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - for(int p=1; p 0 && displace.a > 0) { - float4 oc = base_rgba; - float transparent = oc.a; - int count = 1; - float samples = oc.a; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - // Blur calculations - [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { - [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { - float size = blur_size; - float4 sc; - - if (blue_affects_blur) { - size *= displace.b; + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (chromatic_aberration) { - float4 sc_r = image.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - float4 sc_g = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - float4 sc_b = image.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); - - sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - sc = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - transparent += sc.a; - count++; - base_rgba += sc * sc.a; - samples += sc.a; - } - } +} - //Calculate averages - if (samples > 0.0) - base_rgba /= samples; - base_rgba.a = transparent / count; - } +} - if (blue_affects_colorize) { - base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); - } else { - base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); - } + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSInput { - if (apply_alpha) { - float4 background_rgba = image.Sample(textureSampler, v_in.uv); - return lerp( - float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), - float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), - displace.a - ); - } - - return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] +[Alias('obs.powershell.websocket.RemoveInput')] +param( - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14259,187 +10881,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDisplacementMapInvertShader { +function Remove-OBSProfile { -[Alias('Set-OBSDisplacementMapInvertShader','Add-OBSDisplacementMapInvertShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] +[Alias('obs.powershell.websocket.RemoveProfile')] param( -# Set the displacement_info of OBSDisplacementMapInvertShader -[Alias('displacement_info')] -[ComponentModel.DefaultBindingProperty('displacement_info')] -[String] -$DisplacementInfo, -# Set the displacement_x of OBSDisplacementMapInvertShader -[Alias('displacement_x')] -[ComponentModel.DefaultBindingProperty('displacement_x')] -[Single] -$DisplacementX, -# Set the displacement_y of OBSDisplacementMapInvertShader -[Alias('displacement_y')] -[ComponentModel.DefaultBindingProperty('displacement_y')] -[Single] -$DisplacementY, -# Set the background_layer of OBSDisplacementMapInvertShader -[Alias('background_layer')] -[ComponentModel.DefaultBindingProperty('background_layer')] -[String] -$BackgroundLayer, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'displacement_map_invert' -$ShaderNoun = 'OBSDisplacementMapInvertShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform string displacement_info< - string label = "Displacement"; - string widget_type = "info"; -> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; - -uniform float displacement_x< - string label = "Displacement X (px)"; -> = 16.0; - -uniform float displacement_y< - string label = "Displacement Y (px)"; -> = 16.0; - -uniform texture2d background_layer ; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 map = image.Sample(textureSampler, v_in.uv); - float4 base = background_layer.Sample(textureSampler, v_in.uv); - - float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; - float4 displace = float4( - (map.r * 2) - 1, - (map.g * 2) - 1, - (map.b * 2) - 1, - map.a - ); - - float2 displace_uv = float2(displace.r, displace.g) * displace_strength; - float4 displaced = background_layer.Sample(textureSampler, v_in.uv + displace_uv); - return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14448,187 +10988,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDisplacementMapShader { +function Remove-OBSScene { -[Alias('Set-OBSDisplacementMapShader','Add-OBSDisplacementMapShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] +[Alias('obs.powershell.websocket.RemoveScene')] param( -# Set the displacement_info of OBSDisplacementMapShader -[Alias('displacement_info')] -[ComponentModel.DefaultBindingProperty('displacement_info')] -[String] -$DisplacementInfo, -# Set the displacement_x of OBSDisplacementMapShader -[Alias('displacement_x')] -[ComponentModel.DefaultBindingProperty('displacement_x')] -[Single] -$DisplacementX, -# Set the displacement_y of OBSDisplacementMapShader -[Alias('displacement_y')] -[ComponentModel.DefaultBindingProperty('displacement_y')] -[Single] -$DisplacementY, -# Set the mask_layer of OBSDisplacementMapShader -[Alias('mask_layer')] -[ComponentModel.DefaultBindingProperty('mask_layer')] -[String] -$MaskLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'displacement_map' -$ShaderNoun = 'OBSDisplacementMapShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform string displacement_info< - string label = "Displacement"; - string widget_type = "info"; -> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; - -uniform float displacement_x< - string label = "Displacement X (px)"; -> = 16.0; - -uniform float displacement_y< - string label = "Displacement Y (px)"; -> = 16.0; - -uniform texture2d mask_layer ; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 map = mask_layer.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; - float4 displace = float4( - (map.r * 2) - 1, - (map.g * 2) - 1, - (map.b * 2) - 1, - map.a - ); - float2 displace_uv = float2(displace.r, displace.g) * displace_strength; - float4 displaced = image.Sample(textureSampler, v_in.uv + displace_uv); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14637,236 +11100,233 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDivideRotateShader { +function Remove-OBSSceneItem { -[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] +[Alias('obs.powershell.websocket.RemoveSceneItem')] param( -# Set the iChannel0 of OBSDivideRotateShader -[ComponentModel.DefaultBindingProperty('iChannel0')] -[String] -$IChannel0, -# Set the speed_percentage of OBSDivideRotateShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSDivideRotateShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSDivideRotateShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'divide_rotate' -$ShaderNoun = 'OBSDivideRotateShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// divide and rotate shader for OBS Studio shaderfilter plugin -// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) -// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform texture2d iChannel0; -uniform int speed_percentage< - string label = "Speed"; - string widget_type = "slider"; - int minimum = -10; - int maximum = 10; - int step = 1; -> = 5; -uniform int alpha_percentage< - string label = "Opacity Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = true; -uniform string notes< - string widget_type = "info"; -> = "add rotation and speed"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float2 cm(float2 a, float2 b) { - return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float2 iter(float2 uv, float2 rot, float scale) { - float2 gv = frac(cm(uv, rot) * scale); - float boundDist = 1. - max(abs(gv.x), abs(gv.y)); - float mask = step(.03, boundDist); - gv *= mask; - return gv; } -float4 mainImage(VertData v_in) : TARGET -{ - float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); - float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); - // Normalize coords - //float2 uv = (v_in.uv * uv_scale + uv_offset); - float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; - float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; +} - // Add some time rotation and offset - float t = elapsed_time * speed; - float2 time = float2(sin(t), cos(t)); - uv += time; + +#.ExternalHelp obs-powershell-Help.xml +function Remove-OBSSourceFilter { - // Imaginary component has to be mirrored for natural feeling rotation - mouse.y *= -1.0; - // Draw few layers of this to bend space - float2 rot = cm(mouse, time); - for (float i=1.0; i<=3.0; i++) { - uv = iter(uv, rot, 1.5); - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] +[Alias('obs.powershell.websocket.RemoveSourceFilter')] +param( - // Combine background with new image - float4 background_color = image.Sample(textureSampler, v_in.uv); - float4 col = iChannel0.Sample(textureSampler, uv); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, - // Border - if (uv.x == 0.0 && uv.y == 0.0) { - col = float4(0,0,0,alpha); - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, - // if not appling to alpha layer, set output alpha - if (Apply_To_Alpha_Layer == false) - col.a = alpha; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - //output color is combined with background image - col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +process { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -14875,271 +11335,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDoodleShader { +function Resume-OBSRecord { -[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] +[Alias('obs.powershell.websocket.ResumeRecord')] param( -# Set the ViewProj of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSDoodleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSDoodleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSDoodleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSDoodleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSDoodleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSDoodleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the Doodle_Scale_Percent of OBSDoodleShader -[Alias('Doodle_Scale_Percent')] -[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] -[Single] -$DoodleScalePercent, -# Set the Snap_Percent of OBSDoodleShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] -[Single] -$SnapPercent, -# Set the Notes of OBSDoodleShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'doodle' -$ShaderNoun = 'OBSDoodleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -uniform float4x4 ViewProj; -uniform texture2d image; -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform float Doodle_Scale_Percent< - string label = "Doodle Scale Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 2.5; -uniform float Snap_Percent< - string label = "Snap Percent"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 0.1; -> = 7.5; -uniform string Notes< - string widget_type = "info"; -> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 rand3(float3 co) -{ - float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = frac(512.0*j); - j *= .125; - result.x = frac(512.0*j); - j *= .125; - result.y = frac(512.0*j); - return result - 0.5; -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float snap(float x, float snap) -{ - return snap * round(x / max(0.01,snap)); } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); - float rand = snap(rand_f, Snap_Percent *.01); - float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); - vert_out.uv.xy += noise; - return vert_out; -} +} -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} + +#.ExternalHelp obs-powershell-Help.xml +function Save-OBSReplayBuffer { -technique Draw -{ - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] +[Alias('obs.powershell.websocket.SaveReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15148,276 +11539,146 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrawingsShader { +function Save-OBSSourceScreenshot { -[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] +[Alias('obs.powershell.websocket.SaveSourceScreenshot')] param( -# Set the AngleNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('AngleNum')] -[Int32] -$AngleNum, -# Set the SampNum of OBSDrawingsShader -[ComponentModel.DefaultBindingProperty('SampNum')] -[Int32] -$SampNum, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFormat')] +[string] +$ImageFormat, -process { -$shaderName = 'drawings' -$ShaderNoun = 'OBSDrawingsShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldlcWs +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageFilePath')] +[string] +$ImageFilePath, -uniform int AngleNum< - string label = "Number of angles"; - string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; - int step = 1; -> = 3; -uniform int SampNum< - string label = "Number of samples"; - string widget_type = "slider"; - int minimum = 0.0; - int maximum = 25; - int step = 1; -> = 9; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageWidth')] +[ValidateRange(8,4096)] +[double] +$ImageWidth, -float4 getCol(float2 pos) -{ - // take aspect ratio into account - float2 uv=pos; - float4 c1=image.Sample(textureSampler, uv); - float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); - c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); - float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); - float4 c2=float4(.7,.7,.7,.7); - return min(lerp(c1,c2,1.8*d),.7); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageHeight')] +[ValidateRange(8,4096)] +[double] +$ImageHeight, -float4 getColHT(float2 pos) -{ - return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] +[ValidateRange(-1,100)] +[double] +$ImageCompressionQuality, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float getVal(float2 pos) -{ - float4 c=getCol(pos); - return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; -} -float2 getGrad(float2 pos, float eps) -{ - float2 d=float2(eps,0.); - return float2( - getVal(pos+d.xy)-getVal(pos-d.xy), - getVal(pos+d.yx)-getVal(pos-d.yx) - )/eps/2.; -} +process { - float lum( float3 c) { - return dot(c, float3(0.3, 0.59, 0.11)); - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - float3 clipcolor( float3 c) { - float l = lum(c); - float n = min(min(c.r, c.g), c.b); - float x = max(max(c.r, c.g), c.b); - - if (n < 0.0) { - c.r = l + ((c.r - l) * l) / (l - n); - c.g = l + ((c.g - l) * l) / (l - n); - c.b = l + ((c.b - l) * l) / (l - n); - } - if (x > 1.25) { - c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); - c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); - c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); - } - return c; - } - - float3 setlum( float3 c, float l) { - float d = l - lum(c); - c = c + float3(d,d,d); - return clipcolor(0.85*c); - } - -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - float3 col = float3(0,0,0); - float3 col2 = float3(0,0,0); - float sum=0.; - - for(int i=0;iAdd|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} + + Get-Item $paramCopy["imageFilePath"] | + Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | + Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | + Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru + } @@ -15425,218 +11686,116 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDropShadowShader { +function Send-OBSCallVendorRequest { -[Alias('Set-OBSDropShadowShader','Add-OBSDropShadowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] +[Alias('obs.powershell.websocket.CallVendorRequest')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the shadow_offset_x of OBSDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] -[Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] -[Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] -[Int32] -$ShadowBlurSize, -# Set the notes of OBSDropShadowShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the shadow_color of OBSDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] -[String] -$ShadowColor, -# Set the is_alpha_premultiplied of OBSDropShadowShader -[Alias('is_alpha_premultiplied')] -[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] -[Management.Automation.SwitchParameter] -$IsAlphaPremultiplied, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('vendorName')] +[string] +$VendorName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('requestType')] +[string] +$RequestType, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('requestData')] +[PSObject] +$RequestData, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'drop_shadow' -$ShaderNoun = 'OBSDropShadowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Drop Shadow shader modified by Charles Fettinger -// impose a limiter to keep from crashing the system -//Converted to OpenGL by Exeldro February 19, 2022 -uniform int shadow_offset_x< - string label = "Shadow offset x"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 5; -uniform int shadow_offset_y< - string label = "Shadow offset y"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 5; -uniform int shadow_blur_size< - string label = "Shadow blur size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 15; - int step = 1; -> = 3; -uniform string notes< - string widget_type = "info"; -> = "blur size is limited to a max of 15 to ensure GPU"; -uniform float4 shadow_color; -uniform bool is_alpha_premultiplied; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); - int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); - - float sampled_shadow_alpha = 0.0; - - for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) - { - for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); - return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15645,336 +11804,222 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDrunkShader { +function Send-OBSCustomEvent { -[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] +[Alias('obs.powershell.websocket.BroadcastCustomEvent')] param( -# Set the color_matrix of OBSDrunkShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the glow_percent of OBSDrunkShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSDrunkShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSDrunkShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed_percent of OBSDrunkShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the Apply_To_Alpha_Layer of OBSDrunkShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the glow_color of OBSDrunkShader -[Alias('glow_color')] -[ComponentModel.DefaultBindingProperty('glow_color')] -[String] -$GlowColor, -# Set the ease of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSDrunkShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('eventData')] +[PSObject] +$EventData, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'drunk' -$ShaderNoun = 'OBSDrunkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -uniform float4x4 color_matrix; -uniform int glow_percent< - string label = "Glow percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; -uniform float4 glow_color; -uniform bool ease; -uniform bool glitch; -uniform string notes< - string widget_type = "info"; -> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -// Gaussian Blur -float Gaussian(float x, float o) { - const float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; } -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 offsets[4]; - offsets[0] = float2(-0.05, 0.066); - offsets[1] = float2(-0.05, -0.066); - offsets[2] = float2(0.05, -0.066); - offsets[3] = float2(0.05, 0.066); +} - // convert input for vector math - float blur_amount = float(blur) /100; - float glow_amount = float(glow_percent) * 0.1; - float speed = float(pulse_speed_percent) * 0.01; - float luminance_floor = float(min_brightness) * 0.01; - float luminance_ceiling = float(max_brightness) * 0.01; + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSOffsetMediaInputCursor { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 temp_color = color; - bool glitch_on = glitch; - //circular easing variable - float t = 1 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] +[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +param( - //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); - float4 glitch_color = glow_color; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - for (int n = 0; n < 4; n++){ - //blur sample - b = BlurStyler(t,0,c,d,ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - //test for rand_f color - if (glitch) { - glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); - if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) - { - glitch_on = true; - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] +[double] +$MediaCursorOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance - ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color - glitch_on) //test for rand color - { - //glow calc - if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); - //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); - // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color - // max is used to simulate addition of vector texture color - temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time - max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time - * (glitch_color * (b / 2)); // glow color affected by glow over time - } - } - } - // grab lighter color - return max(color,temp_color); -} +process { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -15983,470 +12028,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSDynamicMaskShader { +function Send-OBSPauseRecord { -[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] +[Alias('obs.powershell.websocket.PauseRecord')] param( -# Set the input_source of OBSDynamicMaskShader -[Alias('input_source')] -[ComponentModel.DefaultBindingProperty('input_source')] -[String] -$InputSource, -# Set the red_base_value of OBSDynamicMaskShader -[Alias('red_base_value')] -[ComponentModel.DefaultBindingProperty('red_base_value')] -[Single] -$RedBaseValue, -# Set the red_red_input_value of OBSDynamicMaskShader -[Alias('red_red_input_value')] -[ComponentModel.DefaultBindingProperty('red_red_input_value')] -[Single] -$RedRedInputValue, -# Set the red_green_input_value of OBSDynamicMaskShader -[Alias('red_green_input_value')] -[ComponentModel.DefaultBindingProperty('red_green_input_value')] -[Single] -$RedGreenInputValue, -# Set the red_blue_input_value of OBSDynamicMaskShader -[Alias('red_blue_input_value')] -[ComponentModel.DefaultBindingProperty('red_blue_input_value')] -[Single] -$RedBlueInputValue, -# Set the red_alpha_input_value of OBSDynamicMaskShader -[Alias('red_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] -[Single] -$RedAlphaInputValue, -# Set the red_multiplier of OBSDynamicMaskShader -[Alias('red_multiplier')] -[ComponentModel.DefaultBindingProperty('red_multiplier')] -[Single] -$RedMultiplier, -# Set the green_base_value of OBSDynamicMaskShader -[Alias('green_base_value')] -[ComponentModel.DefaultBindingProperty('green_base_value')] -[Single] -$GreenBaseValue, -# Set the green_red_input_value of OBSDynamicMaskShader -[Alias('green_red_input_value')] -[ComponentModel.DefaultBindingProperty('green_red_input_value')] -[Single] -$GreenRedInputValue, -# Set the green_green_input_value of OBSDynamicMaskShader -[Alias('green_green_input_value')] -[ComponentModel.DefaultBindingProperty('green_green_input_value')] -[Single] -$GreenGreenInputValue, -# Set the green_blue_input_value of OBSDynamicMaskShader -[Alias('green_blue_input_value')] -[ComponentModel.DefaultBindingProperty('green_blue_input_value')] -[Single] -$GreenBlueInputValue, -# Set the green_alpha_input_value of OBSDynamicMaskShader -[Alias('green_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] -[Single] -$GreenAlphaInputValue, -# Set the green_multiplier of OBSDynamicMaskShader -[Alias('green_multiplier')] -[ComponentModel.DefaultBindingProperty('green_multiplier')] -[Single] -$GreenMultiplier, -# Set the blue_base_value of OBSDynamicMaskShader -[Alias('blue_base_value')] -[ComponentModel.DefaultBindingProperty('blue_base_value')] -[Single] -$BlueBaseValue, -# Set the blue_red_input_value of OBSDynamicMaskShader -[Alias('blue_red_input_value')] -[ComponentModel.DefaultBindingProperty('blue_red_input_value')] -[Single] -$BlueRedInputValue, -# Set the blue_green_input_value of OBSDynamicMaskShader -[Alias('blue_green_input_value')] -[ComponentModel.DefaultBindingProperty('blue_green_input_value')] -[Single] -$BlueGreenInputValue, -# Set the blue_blue_input_value of OBSDynamicMaskShader -[Alias('blue_blue_input_value')] -[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] -[Single] -$BlueBlueInputValue, -# Set the blue_alpha_input_value of OBSDynamicMaskShader -[Alias('blue_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] -[Single] -$BlueAlphaInputValue, -# Set the blue_multiplier of OBSDynamicMaskShader -[Alias('blue_multiplier')] -[ComponentModel.DefaultBindingProperty('blue_multiplier')] -[Single] -$BlueMultiplier, -# Set the alpha_base_value of OBSDynamicMaskShader -[Alias('alpha_base_value')] -[ComponentModel.DefaultBindingProperty('alpha_base_value')] -[Single] -$AlphaBaseValue, -# Set the alpha_red_input_value of OBSDynamicMaskShader -[Alias('alpha_red_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] -[Single] -$AlphaRedInputValue, -# Set the alpha_green_input_value of OBSDynamicMaskShader -[Alias('alpha_green_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] -[Single] -$AlphaGreenInputValue, -# Set the alpha_blue_input_value of OBSDynamicMaskShader -[Alias('alpha_blue_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] -[Single] -$AlphaBlueInputValue, -# Set the alpha_alpha_input_value of OBSDynamicMaskShader -[Alias('alpha_alpha_input_value')] -[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] -[Single] -$AlphaAlphaInputValue, -# Set the alpha_multiplier of OBSDynamicMaskShader -[Alias('alpha_multiplier')] -[ComponentModel.DefaultBindingProperty('alpha_multiplier')] -[Single] -$AlphaMultiplier, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'dynamic-mask' -$ShaderNoun = 'OBSDynamicMaskShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d input_source< - string label = "Input Source"; ->; - -uniform float red_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float red_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float red_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Red Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; - -uniform float green_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float green_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float green_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Green Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; - -uniform float blue_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float blue_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float blue_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Blue Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; - -uniform float alpha_base_value< - string label = "Base Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -uniform float alpha_red_input_value< - string label = "Red Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_green_input_value< - string label = "Green Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_blue_input_value< - string label = "Blue Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_alpha_input_value< - string label = "Alpha Input Value"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float alpha_multiplier< - string label = "Multiplier"; - string widget_type = "slider"; - string group = "Alpha Channel"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - float4 input_color = input_source.Sample(textureSampler, v_in.uv); - float4 mask; - mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; - mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; - mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; - mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; - float4 base = image.Sample(textureSampler, v_in.uv); - return base * mask; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -16455,278 +12130,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEdgeDetectionShader { +function Send-OBSPressInputPropertiesButton { -[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] +[Alias('obs.powershell.websocket.PressInputPropertiesButton')] param( -# Set the sensitivity of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('sensitivity')] -[Single] -$Sensitivity, -# Set the invert_edge of OBSEdgeDetectionShader -[Alias('invert_edge')] -[ComponentModel.DefaultBindingProperty('invert_edge')] -[Management.Automation.SwitchParameter] -$InvertEdge, -# Set the edge_color of OBSEdgeDetectionShader -[Alias('edge_color')] -[ComponentModel.DefaultBindingProperty('edge_color')] -[String] -$EdgeColor, -# Set the edge_multiply of OBSEdgeDetectionShader -[Alias('edge_multiply')] -[ComponentModel.DefaultBindingProperty('edge_multiply')] -[Management.Automation.SwitchParameter] -$EdgeMultiply, -# Set the non_edge_color of OBSEdgeDetectionShader -[Alias('non_edge_color')] -[ComponentModel.DefaultBindingProperty('non_edge_color')] -[String] -$NonEdgeColor, -# Set the non_edge_multiply of OBSEdgeDetectionShader -[Alias('non_edge_multiply')] -[ComponentModel.DefaultBindingProperty('non_edge_multiply')] -[Management.Automation.SwitchParameter] -$NonEdgeMultiply, -# Set the alpha_channel of OBSEdgeDetectionShader -[Alias('alpha_channel')] -[ComponentModel.DefaultBindingProperty('alpha_channel')] -[Management.Automation.SwitchParameter] -$AlphaChannel, -# Set the alpha_level of OBSEdgeDetectionShader -[Alias('alpha_level')] -[ComponentModel.DefaultBindingProperty('alpha_level')] -[Single] -$AlphaLevel, -# Set the alpha_invert of OBSEdgeDetectionShader -[Alias('alpha_invert')] -[ComponentModel.DefaultBindingProperty('alpha_invert')] -[Management.Automation.SwitchParameter] -$AlphaInvert, -# Set the rand_f of OBSEdgeDetectionShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the notes of OBSEdgeDetectionShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('propertyName')] +[string] +$PropertyName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'edge_detection' -$ShaderNoun = 'OBSEdgeDetectionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Edge Detection for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform float sensitivity< - string label = "Sensitivity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.05; -uniform bool invert_edge; -uniform float4 edge_color = {1.0,1.0,1.0,1.0}; -uniform bool edge_multiply; -uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; -uniform bool non_edge_multiply; -uniform bool alpha_channel; -uniform float alpha_level< - string label = "Alpha level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool alpha_invert; -uniform float rand_f; - -uniform string notes< - string widget_type = "info"; -> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; - -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - - float s = 3; - float hstep = uv_pixel_interval.x; - float vstep = uv_pixel_interval.y; - - float offsetx = (hstep * s) / 2.0; - float offsety = (vstep * s) / 2.0; - - float4 lum = float4(0.30, 0.59, 0.11, 1 ); - float samples[9]; - - int index = 0; - for(int i = 0; i < s; i++){ - for(int j = 0; j < s; j++){ - samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); - index++; - } - } - - float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; - float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; - float4 col; - - float o = ((vert * vert) + (hori * hori)); - bool isEdge = o > sensitivity; - if(invert_edge){ - isEdge = !isEdge; - } - if(isEdge) { - col = edge_color; - if(edge_multiply){ - col *= c; - } - } else { - col = non_edge_color; - if(non_edge_multiply){ - col *= c; - } - } - - if (alpha_invert) { - lum = 1.0 - lum; - } - if(alpha_channel){ - if (edge_multiply && isEdge) { - return clamp(lerp(c, col, alpha_level), 0.0, 1.0); - } - else { - // use max instead of multiply - return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); - } - } else { - // col.a = col.a * alpha_level; - return col; - } -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -16735,599 +12247,351 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbersShader { +function Send-OBSSleep { -[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] +[Alias('obs.powershell.websocket.Sleep')] param( -# Set the ViewProj of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSEmbersShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSEmbersShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSEmbersShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSEmbersShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSEmbersShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSEmbersShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSEmbersShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSEmbersShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSEmbersShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the notes of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the Animation_Speed of OBSEmbersShader -[Alias('Animation_Speed')] -[ComponentModel.DefaultBindingProperty('Animation_Speed')] -[Single] -$AnimationSpeed, -# Set the Movement_Direction_Horizontal of OBSEmbersShader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] -[Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSEmbersShader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] -[Single] -$MovementDirectionVertical, -# Set the Movement_Speed_Percent of OBSEmbersShader -[Alias('Movement_Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] -[Int32] -$MovementSpeedPercent, -# Set the Layers_Count of OBSEmbersShader -[Alias('Layers_Count')] -[ComponentModel.DefaultBindingProperty('Layers_Count')] -[Int32] -$LayersCount, -# Set the lumaMin of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSEmbersShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Alpha_Percentage of OBSEmbersShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSEmbersShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sleepMillis')] +[ValidateRange(0,50000)] +[double] +$SleepMillis, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sleepFrames')] +[ValidateRange(0,10000)] +[double] +$SleepFrames, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'embers' -$ShaderNoun = 'OBSEmbersShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -uniform string notes< - string widget_type = "info"; -> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float Animation_Speed < - string label = "Animation Speed"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; - float scale = 1.; -> = 1.5; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 5.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 1.0; -> = 10.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform int Movement_Speed_Percent< - string label = "Movement Speed Percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 5; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -uniform int Layers_Count < - string label = "Layers"; - string widget_type = "slider"; - int minimum = 1.0; - int maximum = 100.0; - int step = 1; -> = 15; -/* ps start -*/ + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } +} -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform bool Apply_To_Alpha_Layer = true; -#define PI 3.1415927 -#define TWO_PI 6.283185 +} -#define PARTICLE_SIZE 0.009 + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSStreamCaption { -#define PARTICLE_SCALE float2(0.5, 1.6) -#define PARTICLE_SCALE_VAR float2(0.25, 0.2) -#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) -#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] +[Alias('obs.powershell.websocket.SendStreamCaption')] +param( -#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 -#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 -#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('captionText')] +[string] +$CaptionText, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -#define SIZE_MOD 1.05 -#define ALPHA_MOD 0.9 -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) -#define Movement_Speed Movement_Speed_Percent * 0.01 -#define UV float2(fragCoord.xy / uv_size) -float hash1_2(float2 x) -{ - return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); -} +process { -float2 hash2_2(float2 x) -{ - mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); - float2 y = mul(x, m); - return fract(sin(y) * 492.194); -} -//Simple interpolated noise -float2 noise2_2(float2 uv) -{ - //float2 f = fract(uv); - float2 f = smoothstep(0.0, 1.0, fract(uv)); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - float2 v00 = hash2_2(uv00); - float2 v01 = hash2_2(uv01); - float2 v10 = hash2_2(uv10); - float2 v11 = hash2_2(uv11); - - float2 v0 = mix(v00, v01, f.y); - float2 v1 = mix(v10, v11, f.y); - float2 v = mix(v0, v1, f.x); - - return v; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -//Simple interpolated noise -float noise1_2(float2 uv) -{ - float2 f = fract(uv); - - float2 uv00 = floor(uv); - float2 uv01 = uv00 + float2(0, 1); - float2 uv10 = uv00 + float2(1, 0); - float2 uv11 = uv00 + 1.0; - - float v00 = hash1_2(uv00); - float v01 = hash1_2(uv01); - float v10 = hash1_2(uv10); - float v11 = hash1_2(uv11); - - float v0 = mix(v00, v01, f.y); - float v1 = mix(v10, v11, f.y); - float v = mix(v0, v1, f.x); - - return v; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) -{ - float noise = 0.0; - float alpha = 1.0; - float size = 1.0; - float2 offset; - for (int i = 0; i < layers; i++) - { - offset += hash2_2(float2(alpha, size)) * 10.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } - //Adding noise with movement - noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; - alpha *= alphaMod; - size *= sizeMod; - } + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); - return noise; -} - -//Rotates point around 0,0 -float2 rotate(float2 vpoint, float deg) -{ - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - return mul(vpoint, m); -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -//Cell center from point on the grid -float2 voronoiPointFromRoot(float2 root, float deg) -{ - float2 vpoint = hash2_2(root) - 0.5; - float s = sin(deg); - float c = cos(deg); - mat2 m = mat2(s, c, -c, s); - vpoint = mul(vpoint, m) * 0.66; - vpoint += root + 0.5; - return vpoint; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -//Voronoi cell point rotation degrees -float degFromRootUV(in float2 uv) -{ - return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; } -float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) -{ - return vpoint + (hash2_2(uv) - 0.5) * range; -} +} -float3 fireParticles(in float2 uv, in float2 originalUV) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float2 rootUV = floor(uv); - float deg = degFromRootUV(rootUV); - float2 pointUV = voronoiPointFromRoot(rootUV, deg); - float dist = 2.0; - float distBloom = 0.0; - - //UV manipulation for the faster particle movement - float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; - tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; + +#.ExternalHelp obs-powershell-Help.xml +function Send-OBSTriggerHotkeyByKeySequence { - //Sparks sdf - dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); - - //Bloom sdf - distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); - //Add sparks - particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; - - //Add bloom - particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] +[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] +param( - //Upper disappear curve randomization - float border = (hash1_2(rootUV) - 0.5) * 2.0; - float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); - - //Lower appear curve randomization - border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; - float appear = smoothstep(border, border + 0.4, originalUV.y); - - return particles * disappear * appear; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyId')] +[string] +$KeyId, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers')] +[PSObject] +$KeyModifiers, -//Layering particles to imitate 3D view -float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) -{ - float3 particles = float3(0.0, 0.0, 0.0); - float size = 1.0; - float alpha = 1.0; - float2 offset = float2(0.0, 0.0); - float2 noiseOffset; - float2 bokehUV; - - for (int i = 0; i < layers; i++) - { - //Particle noise movement - noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; - - //UV with applied movement - bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; - - //Adding particles if there is more smoke, remove smaller particles - particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); - - //Moving uv origin to avoid generating the same particles - offset += hash2_2(float2(alpha, alpha)) * 10.0; - - alpha *= alphaMod; - size *= sizeMod; - } - - return particles; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] +[switch] +$KeyModifiersshift, +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.control')] +[switch] +$KeyModifierscontrol, -void mainImage(out float4 fragColor, in float2 fragCoord) -{ - float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; - float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); - - uv *= 1.8; - float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); - - float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); - smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); - float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; - - //Cutting holes in smoke - smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), - 2.0) * 1.5; - - float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); - - float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); - col.rgb *= vignette; - col.rgb = smoothstep(-0.08, 1.0, col.rgb); - - if (Apply_To_Alpha_Layer) - { - float4 original_color = image.Sample(textureSampler, UV); - - float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min, 0.0, 1.0); - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] +[switch] +$KeyModifiersalt, - fragColor = col; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('keyModifiers.command')] +[switch] +$KeyModifierscommand, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -/*ps end*/ -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +process { -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17336,242 +12600,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossColorShader { +function Send-OBSTriggerHotkeyByName { -[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] +[Alias('obs.powershell.websocket.TriggerHotkeyByName')] param( -# Set the Angle_Steps of OBSEmbossColorShader -[Alias('Angle_Steps')] -[ComponentModel.DefaultBindingProperty('Angle_Steps')] -[Int32] -$AngleSteps, -# Set the Radius_Steps of OBSEmbossColorShader -[Alias('Radius_Steps')] -[ComponentModel.DefaultBindingProperty('Radius_Steps')] -[Int32] -$RadiusSteps, -# Set the ampFactor of OBSEmbossColorShader -[ComponentModel.DefaultBindingProperty('ampFactor')] -[Single] -$AmpFactor, -# Set the Up_Down_Percent of OBSEmbossColorShader -[Alias('Up_Down_Percent')] -[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] -[Int32] -$UpDownPercent, -# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the notes of OBSEmbossColorShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('hotkeyName')] +[string] +$HotkeyName, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('contextName')] +[string] +$ContextName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'emboss_color' -$ShaderNoun = 'OBSEmbossColorShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int Angle_Steps< - string label = "Angle Steps"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 9; // -uniform int Radius_Steps< - string label = "Radius Steps"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 20; - int step = 1; -> = 4; // -uniform float ampFactor< - string label = "amp Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 12.0; -uniform int Up_Down_Percent< - string label = "Up Down Percent"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 0; -uniform bool Apply_To_Alpha_Layer = true; -uniform string notes< - string widget_type = "info"; -> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; - -float4 mainImage(VertData v_in) : TARGET -{ - float radiusSteps = clamp(Radius_Steps, 0, 20); - float angleSteps = clamp(Angle_Steps, 1, 20); - float PI = 3.1415926535897932384626433832795;//acos(-1); - int totalSteps = int(radiusSteps * angleSteps); - float minRadius = (1 * uv_pixel_interval.y); - float maxRadius = (6 * uv_pixel_interval.y); - - float angleDelta = ((2 * PI) / angleSteps); - float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - float embossAngle = 0.25 * PI; - - float4 c0 = image.Sample(textureSampler, v_in.uv); - float4 origColor = c0; - float4 accumulatedColor = float4(0,0,0,0); - - if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) - { - for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { - float radius = minRadius + radiusStep * radiusDelta; - - for (float angle = 0; angle < (2 * PI); angle += angleDelta) { - float2 currentCoord; - - float xDiff = radius * cos(angle); - float yDiff = radius * sin(angle); - - currentCoord = v_in.uv + float2(xDiff, yDiff); - float4 currentColor = image.Sample(textureSampler, currentCoord); - float4 colorDiff = abs(c0 - currentColor); - float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); - accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; - } - } - accumulatedColor *= ampFactor; - - c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); - } - //return c0 + accumulatedColor; // down; - //return c0 - accumulatedColor; // up - return c0; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17580,177 +12712,115 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSEmbossShader { +function Send-OBSTriggerMediaInputAction { -[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] +[Alias('obs.powershell.websocket.TriggerMediaInputAction')] param( -# Set the Use_Color of OBSEmbossShader -[Alias('Use_Color')] -[ComponentModel.DefaultBindingProperty('Use_Color')] -[Management.Automation.SwitchParameter] -$UseColor, -# Set the Apply_To_Alpha_Layer of OBSEmbossShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaAction')] +[string] +$MediaAction, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'emboss' -$ShaderNoun = 'OBSEmbossShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 -uniform bool Use_Color; -uniform bool Apply_To_Alpha_Layer = true; - -float4 mainImage(VertData v_in) : TARGET -{ - - float dx = 1 / uv_size.x; - float dy = 1 / uv_size.y; - float4 c0 = image.Sample(textureSampler, v_in.uv); - if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) - { - float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); - float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); - float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); - float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); - float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); - float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); - c0 = (-c1 - c2 - c4 + c6 + c8 + c9); - float c = (c0.r + c0.g + c0.b) / 3 + 0.5; - c0 = float4(c,c,c,c); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (Use_Color) - { - float4 rgba = image.Sample(textureSampler, v_in.uv); - return (0.5 * rgba) + c0; - } - } - return c0; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -17759,300 +12829,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSExeldroBentCameraShader { +function Send-OBSTriggerStudioModeTransition { -[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] +[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] param( -# Set the left_side_width of OBSExeldroBentCameraShader -[Alias('left_side_width')] -[ComponentModel.DefaultBindingProperty('left_side_width')] -[Single] -$LeftSideWidth, -# Set the left_side_size of OBSExeldroBentCameraShader -[Alias('left_side_size')] -[ComponentModel.DefaultBindingProperty('left_side_size')] -[Single] -$LeftSideSize, -# Set the left_side_shadow of OBSExeldroBentCameraShader -[Alias('left_side_shadow')] -[ComponentModel.DefaultBindingProperty('left_side_shadow')] -[Single] -$LeftSideShadow, -# Set the left_flip_width of OBSExeldroBentCameraShader -[Alias('left_flip_width')] -[ComponentModel.DefaultBindingProperty('left_flip_width')] -[Single] -$LeftFlipWidth, -# Set the left_flip_shadow of OBSExeldroBentCameraShader -[Alias('left_flip_shadow')] -[ComponentModel.DefaultBindingProperty('left_flip_shadow')] -[Single] -$LeftFlipShadow, -# Set the right_side_width of OBSExeldroBentCameraShader -[Alias('right_side_width')] -[ComponentModel.DefaultBindingProperty('right_side_width')] -[Single] -$RightSideWidth, -# Set the right_side_size of OBSExeldroBentCameraShader -[Alias('right_side_size')] -[ComponentModel.DefaultBindingProperty('right_side_size')] -[Single] -$RightSideSize, -# Set the right_side_shadow of OBSExeldroBentCameraShader -[Alias('right_side_shadow')] -[ComponentModel.DefaultBindingProperty('right_side_shadow')] -[Single] -$RightSideShadow, -# Set the right_flip_width of OBSExeldroBentCameraShader -[Alias('right_flip_width')] -[ComponentModel.DefaultBindingProperty('right_flip_width')] -[Single] -$RightFlipWidth, -# Set the right_flip_shadow of OBSExeldroBentCameraShader -[Alias('right_flip_shadow')] -[ComponentModel.DefaultBindingProperty('right_flip_shadow')] -[Single] -$RightFlipShadow, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'exeldro-bent-camera' -$ShaderNoun = 'OBSExeldroBentCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float left_side_width< - string label = "Left side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float left_side_size< - string label = "Left side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float left_side_shadow< - string label = "Left side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float left_flip_width< - string label = "Left flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float left_flip_shadow< - string label = "Left flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; - -uniform float right_side_width< - string label = "Right side width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; -uniform float right_side_size< - string label = "Right side size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform float right_side_shadow< - string label = "Right side shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.8; -uniform float right_flip_width< - string label = "Right flip width"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; -uniform float right_flip_shadow< - string label = "Right flip shadow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos=v_in.uv; - float shadow = 1.0; - if(pos.x < left_side_width){ - pos.y -= 0.5; - pos.y /= left_side_size; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width / 2.0; - pos.x /= left_side_size; - pos.x += left_side_width + left_flip_width / 2.0; - shadow = left_side_shadow; - }else if(pos.x < left_side_width + left_flip_width){ - float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= left_side_width + left_flip_width; - pos.x /= factor; - pos.x += left_side_width + left_flip_width; - shadow = left_flip_shadow; - } - if(1.0 - pos.x < right_side_width){ - pos.y -= 0.5; - pos.y /= right_side_size; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); - pos.x /= right_side_size; - pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); - shadow = right_side_shadow; - }else if(1.0 - pos.x < right_side_width + right_flip_width){ - float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); - pos.y -= 0.5; - pos.y /= factor; - pos.y += 0.5; - pos.x -= 1.0 - (right_side_width + right_flip_width); - pos.x /= factor; - pos.x += 1.0 -(right_side_width + right_flip_width); - shadow = right_flip_shadow; - } - float4 p_color = image.Sample(textureSampler, pos); - p_color.rgb *= shadow; - return p_color; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18061,173 +12931,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFadeTransitionShader { +function Set-OBSCurrentPreviewScene { -[Alias('Set-OBSFadeTransitionShader','Add-OBSFadeTransitionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] +[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] param( -# Set the image_a of OBSFadeTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSFadeTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSFadeTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSFadeTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fade-transition' -$ShaderNoun = 'OBSFadeTransitionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -float4 mainImage(VertData v_in) : TARGET -{ - float4 a_val = image_a.Sample(textureSampler, v_in.uv); - float4 b_val = image_b.Sample(textureSampler, v_in.uv); - float4 rgba = lerp(a_val, b_val, transition_time); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18236,230 +13043,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorGradientShader { +function Set-OBSCurrentProfile { -[Alias('Set-OBSFillColorGradientShader','Add-OBSFillColorGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] +[Alias('obs.powershell.websocket.SetCurrentProfile')] param( -# Set the Fill of OBSFillColorGradientShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Gradient_Width of OBSFillColorGradientShader -[Alias('Gradient_Width')] -[ComponentModel.DefaultBindingProperty('Gradient_Width')] -[Single] -$GradientWidth, -# Set the Gradient_Offset of OBSFillColorGradientShader -[Alias('Gradient_Offset')] -[ComponentModel.DefaultBindingProperty('Gradient_Offset')] -[Single] -$GradientOffset, -# Set the Fill_Direction of OBSFillColorGradientShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill_Color of OBSFillColorGradientShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('profileName')] +[string] +$ProfileName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_gradient' -$ShaderNoun = 'OBSFillColorGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.005; -> = 0.500; - -uniform float Gradient_Width< - string label = "Gradient Width"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 0.15; // Adjust the maximum value as needed - float step = 0.01; -> = 0.05; - -uniform float Gradient_Offset< - string label = "Gradient Offset"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 0.100; // Adjust the maximum value as needed - float step = 0.005; -> = 0.00; - -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Bottom"; - int option_3_value = 3; - string option_3_label = "Top"; -> = 0; - -uniform float4 Fill_Color; - -float4 mainImage(VertData v_in) : TARGET -{ - float distanceToEdge = 0.0; - - // Calculate distance to the fill edge based on the selected direction - if (Fill_Direction == 0) - distanceToEdge = Fill - v_in.uv.x; - else if (Fill_Direction == 1) - distanceToEdge = v_in.uv.x - (1.0 - Fill); - else if (Fill_Direction == 2) - distanceToEdge = v_in.uv.y - (1.0 - Fill); - else if (Fill_Direction == 3) - distanceToEdge = Fill - v_in.uv.y; - - // Calculate the gradient factor based on the distance to the edge and the gradient width - float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); - float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; - - // Adjust distanceToEdge by the Gradient_Offset - distanceToEdge += gradientOffset; - - // Normalize the distance to be between 0 and 1 - distanceToEdge = saturate(distanceToEdge); - - // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); - // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); - float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); - - // Blend between the fill color and the original image color using the gradient factor - float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); - return finalColor; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18468,195 +13150,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorLinearShader { +function Set-OBSCurrentProgramScene { -[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] +[Alias('obs.powershell.websocket.SetCurrentProgramScene')] param( -# Set the Fill of OBSFillColorLinearShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Fill_Direction of OBSFillColorLinearShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill_Color of OBSFillColorLinearShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_linear' -$ShaderNoun = 'OBSFillColorLinearShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.005; ->; -uniform int Fill_Direction< - string label = "Fill from:"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Left"; - int option_1_value = 1; - string option_1_label = "Right"; - int option_2_value = 2; - string option_2_label = "Top"; - int option_3_value = 3; - string option_3_label = "Bottom"; -> = 0; -uniform float4 Fill_Color; -float4 mainImage(VertData v_in) : TARGET -{ - bool is_inside_fill = true; - - // Check if the pixel is within the specified "fill width" on the left side - if(Fill_Direction == 0){ - is_inside_fill = v_in.uv.x > Fill; - } - if(Fill_Direction == 1) - { - is_inside_fill = v_in.uv.x < (1.0 - Fill); - } - if(Fill_Direction == 2) - { - is_inside_fill = v_in.uv.y > Fill; - } - if(Fill_Direction == 3) - { - is_inside_fill = v_in.uv.y < (1.0 - Fill); - } - - // Invert is_inside_fill - is_inside_fill = !is_inside_fill; - - // If inside the "fill," make the pixel selected colour; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18665,249 +13262,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialDegreesShader { +function Set-OBSCurrentSceneCollection { -[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] +[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] param( -# Set the Fill_Direction of OBSFillColorRadialDegreesShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialDegreesShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialDegreesShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] -[Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialDegreesShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialDegreesShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialDegreesShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneCollectionName')] +[string] +$SceneCollectionName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_radial_degrees' -$ShaderNoun = 'OBSFillColorRadialDegreesShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; - -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 360; - float step = 1.00000; ->; - -uniform float Start_Angle< - string label = "Start Angle"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; - -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float Offset_Y< - string label = "Offset Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float4 Fill_Color; - -float4 mainImage(VertData v_in) : TARGET -{ - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); - - // Convert angle from radians to degrees - angle = degrees(angle); - - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; - } - - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; - - // Check if the angle is within the specified "fill width" - bool is_inside_fill = angle < Fill; - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -18916,252 +13369,213 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFillColorRadialPercentageShader { +function Set-OBSCurrentSceneTransition { -[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] param( -# Set the Fill_Direction of OBSFillColorRadialPercentageShader -[Alias('Fill_Direction')] -[ComponentModel.DefaultBindingProperty('Fill_Direction')] -[Int32] -$FillDirection, -# Set the Fill of OBSFillColorRadialPercentageShader -[ComponentModel.DefaultBindingProperty('Fill')] -[Single] -$Fill, -# Set the Start_Angle of OBSFillColorRadialPercentageShader -[Alias('Start_Angle')] -[ComponentModel.DefaultBindingProperty('Start_Angle')] -[Single] -$StartAngle, -# Set the Offset_X of OBSFillColorRadialPercentageShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSFillColorRadialPercentageShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the Fill_Color of OBSFillColorRadialPercentageShader -[Alias('Fill_Color')] -[ComponentModel.DefaultBindingProperty('Fill_Color')] -[String] -$FillColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fill_color_radial_percentage' -$ShaderNoun = 'OBSFillColorRadialPercentageShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.141592653589793238 - -uniform int Fill_Direction< - string label = "Fill Direction"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Clockwise"; - int option_1_value = 1; - string option_1_label = "Counter-Clockwise"; -> = 0; -uniform float Fill< - string label = "Fill"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.00005; -> = 0.0; -uniform float Start_Angle< - string label = "Start Angle"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 720; - float step = 1.00000; -> = 360.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float Offset_Y< - string label = "Offset Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float4 Fill_Color; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float4 mainImage(VertData v_in) : TARGET -{ - // Calculate the center of the screen based on aspect ratio - float aspectRatioX = uv_size.x / uv_size.y; - float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // Normalize the UV coordinates based on aspect ratio - float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); +} - // Calculate the direction vector from the center to the current pixel - float2 dir = normalizedUV - center; - // Calculate the angle in radians - float angle = atan2(dir.y, dir.x); +} - // Convert angle from radians to degrees - angle = degrees(angle); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSCurrentSceneTransitionDuration { - // Offset the angle to start from the specified starting angle - angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock - if (angle >= 360.0) - angle -= 360.0; - // Adjust the angle based on the selected fill direction - if (Fill_Direction == 1) { - // Counter-clockwise fill - angle = 360.0 - angle; - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] +param( - // Ensure angle is within [0, 360] range - if (angle < 0.0) - angle += 360.0; - else if (angle >= 360.0) - angle -= 360.0; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - // Calculate the percentage of the angle - float anglePercentage = angle / 360.0; - // Check if the angle percentage is within the specified "fill percentage" - bool is_inside_fill = anglePercentage < Fill; +process { - // If inside the "fill," make the pixel selected color; otherwise, use the original image color - return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -19170,259 +13584,228 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFilterTemplateShader { +function Set-OBSCurrentSceneTransitionSettings { -[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] +[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] param( -# Set the ViewProj of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSFilterTemplateShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFilterTemplateShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFilterTemplateShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFilterTemplateShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFilterTemplateShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFilterTemplateShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSFilterTemplateShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFilterTemplateShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFilterTemplateShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the notes of OBSFilterTemplateShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionSettings')] +[PSObject] +$TransitionSettings, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'filter_template' -$ShaderNoun = 'OBSFilterTemplateShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//My shader modified by Me for use with obs-shaderfilter month/year v.02 -//Section to converting GLSL to HLSL - can delete if unneeded -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float4x4 ViewProj; -uniform texture2d image; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ -uniform string notes< - string widget_type = "info"; -> = "add notes here"; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); } -/* -**Shaders use the built in Draw technique** -**this section can be deleted** -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} -*/ +} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioBalance { - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] +[Alias('obs.powershell.websocket.SetInputAudioBalance')] +param( - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioBalance')] +[ValidateRange(0,1)] +[double] +$InputAudioBalance, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -19431,699 +13814,467 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFire3Shader { +function Set-OBSInputAudioMonitorType { -[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] +[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] param( -# Set the ViewProj of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSFire3Shader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSFire3Shader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSFire3Shader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSFire3Shader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSFire3Shader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSFire3Shader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSFire3Shader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSFire3Shader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSFire3Shader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the Movement_Direction_Horizontal of OBSFire3Shader -[Alias('Movement_Direction_Horizontal')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] -[Single] -$MovementDirectionHorizontal, -# Set the Movement_Direction_Vertical of OBSFire3Shader -[Alias('Movement_Direction_Vertical')] -[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] -[Single] -$MovementDirectionVertical, -# Set the Alpha_Percentage of OBSFire3Shader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] -$Speed, -# Set the Invert of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFire3Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFire3Shader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFire3Shader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSFire3Shader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSFire3Shader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Full_Width of OBSFire3Shader -[Alias('Full_Width')] -[ComponentModel.DefaultBindingProperty('Full_Width')] -[Management.Automation.SwitchParameter] -$FullWidth, -# Set the Flame_Size of OBSFire3Shader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Single] -$FlameSize, -# Set the Spark_Grid_Height of OBSFire3Shader -[Alias('Spark_Grid_Height')] -[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] -[Single] -$SparkGridHeight, -# Set the Flame_Modifier of OBSFire3Shader -[Alias('Flame_Modifier')] -[ComponentModel.DefaultBindingProperty('Flame_Modifier')] -[Single] -$FlameModifier, -# Set the Flame_Tongue_Size of OBSFire3Shader -[Alias('Flame_Tongue_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] -[Single] -$FlameTongueSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('monitorType')] +[string] +$MonitorType, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fire-3' -$ShaderNoun = 'OBSFire3Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//My effect modified by Me for use with obs-shaderfilter month/year v.02 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform float Movement_Direction_Horizontal< - string label = "Movement Direction Horizontal"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; -uniform float Movement_Direction_Vertical< - string label = "Movement Direction Vertical"; - string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.01; -> = 0.0; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -#define iTime elapsed_time -#define iResolution float4(uv_size,uv_pixel_interval) -#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform int Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 80; -uniform bool Invert = false; -uniform float lumaMin< - string label = "Luma Min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.04; -uniform bool Apply_To_Image = true; -uniform bool Replace_Image_Color = true; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color = false; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - float2 uv = v_in.uv; - if(Invert) - uv = 1.0 - v_in.uv; - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; } -int2 iMouse() -{ - return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); -} +} -float mod(float x, float y) -{ - return x - y * floor(x / y); -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioSyncOffset { -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x / y); -} -/*ps start*/ -#define PI 3.1415926535897932384626433832795 -uniform bool Full_Width = false; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] +[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] +param( -uniform float Flame_Size< - string label = "Flame Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, -uniform float Spark_Grid_Height< - string label = "Spark Grid Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, -uniform float Flame_Modifier< - string label = "Flame Modifier"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] +[ValidateRange(-950,20000)] +[double] +$InputAudioSyncOffset, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -uniform float Flame_Tongue_Size< - string label = "Flame Tongue Size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 8.5; -// -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// +process { -vec3 mod2893(vec3 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} -vec4 mod289(vec4 x) -{ - return x - floor(x * (1.0 / 289.0)) * 289.0; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -vec4 permute(vec4 x) -{ - return mod289(((x * 34.0) + 1.0) * x); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -vec4 taylorInvSqrt(vec4 r) -{ - return 1.79284291400159 - 0.85373472095314 * r; } -float snoise(vec3 v) -{ - const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); -// First corner - vec3 i = floor(v + dot(v, C.yyy)); - vec3 x0 = v - i + dot(i, C.xxx); +} -// Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min(g.xyz, l.zxy); - vec3 i2 = max(g.xyz, l.zxy); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputAudioTracks { - // x0 = x0 - 0.0 + 0.0 * C.xxx; - // x1 = x0 - i1 + 1.0 * C.xxx; - // x2 = x0 - i2 + 2.0 * C.xxx; - // x3 = x0 - 1.0 + 3.0 * C.xxx; - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y -// Permutations - i = mod2893(i); - vec4 p = permute(permute(permute( - i.z + vec4(0.0, i1.z, i2.z, 1.0)) - + i.y + vec4(0.0, i1.y, i2.y, 1.0)) - + i.x + vec4(0.0, i1.x, i2.x, 1.0)); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] +[Alias('obs.powershell.websocket.SetInputAudioTracks')] +param( -// Gradients: 7x7 points over a square, mapped onto an octahedron. -// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputAudioTracks')] +[PSObject] +$InputAudioTracks, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - vec4 x = x_ * ns.x + ns.yyyy; - vec4 y = y_ * ns.x + ns.yyyy; - vec4 h = 1.0 - abs(x) - abs(y); - vec4 b0 = vec4(x.xy, y.xy); - vec4 b1 = vec4(x.zw, y.zw); +process { - //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; - //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; - vec4 s0 = floor(b0) * 2.0 + 1.0; - vec4 s1 = floor(b1) * 2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); - vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; - vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - vec3 p0 = vec3(a0.xy, h.x); - vec3 p1 = vec3(a0.zw, h.y); - vec3 p2 = vec3(a1.xy, h.z); - vec3 p3 = vec3(a1.zw, h.w); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -//Normalise gradients - //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); - vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); - p0 *= norm.x; - p1 *= norm.y; - p2 *= norm.z; - p3 *= norm.w; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -// Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); - m = m * m; - return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -////////////////////////////////////////////////////////////// + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -// PRNG -// From https://www.shadertoy.com/view/4djSRW -float prng(in vec2 seed) -{ - seed = fract(seed * vec2(5.3983, 5.4427)); - seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); - return fract(seed.x * seed.y * 95.4337); } -////////////////////////////////////////////////////////////// -float noiseStack(vec3 pos, int octaves, float falloff) -{ - float noise = snoise(vec3(pos)); - float off = 1.0; - if (octaves > 1) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 2) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - if (octaves > 3) - { - pos *= 2.0; - off *= falloff; - noise = (1.0 - off) * noise + off * snoise(vec3(pos)); - } - return (1.0 + noise) / 2.0; -} +} -vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) -{ - float displaceA = noiseStack(pos, octaves, falloff); - float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); - return vec2(displaceA, displaceB); -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputMute { -float4 mainImage(VertData v_in) : TARGET -{ - float2 UV = (1.0 - v_in.uv) * uv_scale; - if (Invert) - UV = v_in.uv * uv_scale; - float alpha = saturate(Alpha_Percentage * .01); - float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); - - vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); - if (Full_Width) - { - resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; - - } - resolution.x = mul(resolution.x, 1 / 1); - float time = iTime * (Speed * 0.01); - //vec2 drag = iMouse().xy; - vec2 offset = iMouse().xy; - // - float xpart = UV.x / resolution.x; - float ypart = UV.y / resolution.y; - // - - float ypartClip = UV.y / ( flame_size * 75.0); - float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); - float ypartClipped = min(ypartClip, 1.0); - float ypartClippedn = (1 - ypartClipped); - // - float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); - // - float timeSpeed = 0.5 * (Speed * 0.01); - float realTime = -1.0 * timeSpeed * time; - // - vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; - vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); - vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); - vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; - // - vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); - vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); - // - vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; - float noise = noiseStack(noiseCoord, 3, 0.4); - // - float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); - // - float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); - float fff = f * f * f; - vec3 fire = 1.5 * vec3(f, fff, fff * fff); - // - // smoke - float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; - float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); - vec3 smoke = vec3(smokePart, smokePart, smokePart); - // - // sparks - float sparkGridSize = Spark_Grid_Height; - vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); - sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); - sparkCoord += 100.0 * flow.xy; - if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) - sparkCoord.x += 0.5 * sparkGridSize; - vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); - float sparkRandom = prng( sparkGridIndex); - float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); - vec3 sparks = vec3(0.0, 0.0, 0.0); - if (sparkLife > 0.0) - { - float sparkSize = xfuel * xfuel * sparkRandom * 0.08; - float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; - vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); - vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; - vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); - float sparkLength = length(sparkModulus); - float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); - sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); - } - // - float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire, 0.0, alpha); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); - } - else - { - rgba = color; - } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - - return rgba; -} -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] +[Alias('obs.powershell.websocket.SetInputMute')] +param( -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputMuted')] +[switch] +$InputMuted, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20132,434 +14283,237 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireShader { +function Set-OBSInputName { -[Alias('Set-OBSFireShader','Add-OBSFireShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] +[Alias('obs.powershell.websocket.SetInputName')] param( -# Set the Alpha_Percentage of OBSFireShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Int32] -$AlphaPercentage, -# Set the Speed of OBSFireShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Int32] -$Speed, -# Set the Flame_Size of OBSFireShader -[Alias('Flame_Size')] -[ComponentModel.DefaultBindingProperty('Flame_Size')] -[Int32] -$FlameSize, -# Set the Fire_Type of OBSFireShader -[Alias('Fire_Type')] -[ComponentModel.DefaultBindingProperty('Fire_Type')] -[Int32] -$FireType, -# Set the Invert of OBSFireShader -[ComponentModel.DefaultBindingProperty('Invert')] -[Management.Automation.SwitchParameter] -$Invert, -# Set the lumaMin of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMin')] -[Single] -$LumaMin, -# Set the lumaMinSmooth of OBSFireShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] -[Single] -$LumaMinSmooth, -# Set the Apply_To_Image of OBSFireShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSFireShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSFireShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSFireShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Notes of OBSFireShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newInputName')] +[string] +$NewInputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fire' -$ShaderNoun = 'OBSFireShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 -// https://github.com/Oncorporation/obs-shaderfilter plugin -// https://www.shadertoy.com/view/MtcGD7 original version -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -//v.5 -// flicker -// flame type -// apply to image -// replace image color -// speed -// flame size -// alpha -// invert direction/position -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int Alpha_Percentage< - string label = "Aplha Percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int Speed< - string label = "Speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 100; -uniform int Flame_Size< - string label = "Flame Size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 70; -uniform int Fire_Type< - string label = "Fire Type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Smaller and more whisps"; - int option_1_value = 1; - string option_1_label = "Larger and more volume"; -> = 1; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform bool Invert < - string name = "Invert"; -> = false; -uniform float lumaMin< - string label = "Luma min"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma min smooth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.04; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string Notes< - string widget_type = "info"; -> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -vec3 rgb2hsv(vec3 c) -{ - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -vec3 hsv2rgb(vec3 c) -{ - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -float rand(vec2 n) -{ - return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); - //return sin(rand_f, n); -} -float noise(vec2 n) -{ - const vec2 d = vec2(0.0, 1.0); - vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); - return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); -} - -float fbm(vec2 n) -{ - float total = 0.0, amplitude = 1.0; - for (int i = 0; i < 5; i++) - { - total += noise(n) * amplitude; - n += n * 1.7; - amplitude *= 0.47; - } - return total; -} +} -float4 mainImage(VertData v_in) : TARGET -{ - float2 iResolution = uv_scale; - float flame_size = clamp(Flame_Size * .01,-5,5); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSInputSettings { - // inverting direction is logically inverted to allow the bottom up to be normal - float fire_base = (v_in.uv.y / iResolution.y); - float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); - float direction = -1.0 * clamp(Speed*.01,-5,5); - if (!Invert) - { - direction *= -1.0; - fire_base = 1 - fire_base; - fire_pix = 1 - fire_pix; - } - float iTime = direction * elapsed_time; - - const vec3 c1 = vec3(0.5, 0.0, 0.1); - const vec3 c2 = vec3(0.9, 0.1, 0.0); - const vec3 c3 = vec3(0.2, 0.1, 0.7); - const vec3 c4 = vec3(1.0, 0.9, 0.1); - const vec3 c5 = vec3(0.1, 0.1, 0.1); - const vec3 c6 = vec3(0.9, 0.9, 0.9); - vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); - float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; - float alpha = saturate(Alpha_Percentage * .01); - - //change the constant term for all kinds of cool distance versions, - //make plus/minus to switch between - //ground fire and fire rain! - float dist = 3.5 - sin(iTime * 0.4) / 1.89; - - vec2 p = fire_pix * dist / iResolution.xx; - p.x -= iTime / 1.1; - float3 black = float3(0,0,0); - vec3 fire; +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] +[Alias('obs.powershell.websocket.SetInputSettings')] +param( - if (Fire_Type == 1) - { - //fire version 1 larger and more volume - float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); - float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); - float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; - float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; - q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), - fbm(p - q - iTime* speed.y)) ; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - fire += .05; - fire.r *= .8; - vec3 hsv = rgb2hsv(fire); - hsv.y *= hsv.z * 1.1; - hsv.z *= hsv.y * 1.13; - hsv.y = (2.2 - hsv.z * .9) * 1.20; - fire = hsv2rgb(hsv); - } - else - { - // fire version 0 - smaller and more whisps - p += (rand_f *.01); - float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); - float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); - float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; - float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; - float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; - q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputSettings')] +[PSObject] +$InputSettings, - vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), - fbm(p + q - iTime * speed.y)) * shift; - vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); - //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); - - fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); - fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); - fire = fire/(1.0+max(black,fire)); - } - float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); - - // remove dark areas per user - float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); - float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); - rgba.a = clamp(luma_min_fire,0.0,alpha); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - if (color.a > 0.0) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); - } - else - { - rgba = color; - } - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - return rgba; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) +process { -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20568,256 +14522,240 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworks2Shader { +function Set-OBSInputVolume { -[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] +[Alias('obs.powershell.websocket.SetInputVolume')] param( -# Set the Speed of OBSFireworks2Shader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeMul')] +[ValidateRange(0,20)] +[double] +$InputVolumeMul, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputVolumeDb')] +[ValidateRange(-100,26)] +[double] +$InputVolumeDb, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fireworks2' -$ShaderNoun = 'OBSFireworks2Shader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// based on https://www.shadertoy.com/view/4dBGRw -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 1.0; -> = 100.0; -#ifndef OPENGL -#define mat2 float2x2 -#define mix lerp -float mod(float x, float y) -{ - return x - y * floor(x / y); -} -#endif -//Creates a diagonal red-and-white striped pattern. -float3 barberpole(float2 pos, float2 rocketpos){ - float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); - float3 col=float3(1.0,1.0,1.0); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - d = mod(d*20.,2.0); - if(d>1.0){ - col=float3(1.0,0.0,0.0); - } - return col; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float4 rocket(float2 pos, float2 rocketpos){ - float4 col = float4(0.0,0.0,0.0,0.0); - float f = 0.; - float absx= abs(rocketpos.x - pos.x); - float absy = abs(rocketpos.y-pos.y); - //wooden stick - if(absx<0.01&&absy<0.22){ - col=float4(1.0,0.5,0.5,1.0); - } - - //Barberpole - - if(absx<0.05&&absy<0.15){ - col=float4(barberpole(pos, rocketpos),1.0); - } - //Rocket Point - float pointw=(rocketpos.y-pos.y-0.25)*-0.7; - if((rocketpos.y-pos.y)>0.1){ - f=smoothstep(pointw-0.001,pointw+0.001,absx); - - col=mix(float4(1.0,0.0,0.0,1.0),col, f); - } - //Shadow - - f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); - col.rgb *= 0.7+f; - - return col; -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float rand(float val, float seed){ - return cos(val*sin(val*seed)*seed); } -float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSMediaInputCursor { -float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ - float4 col= float4(0.0,0.0,0.0,0.0); - float2 pp = float2(1.0,0.0); - mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); - for(float i=1.0;i<=128.0;i++){ - float d=rand(i, seed); - float fade=(i/128.0)*time; - float2 particpos = cpos + time*pp*d; - pp = mul(rr,pp); - col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); - } - col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); - col.a = col.r+col.g+col.b; - return col; -} -float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ - - float timeoffset = 2.0; - float4 col=float4(0.0,0.0,0.0,0.0); - if(time<=0.){ - return col; - } - time *= Speed /100.0; - if(mod(time, 6.0)>timeoffset){ - col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); - }else{ - - col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); - } - return col; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; - uv.y = -uv.y; - uv.x *= uv_size.x/uv_size.y; - float4 col = image.Sample(textureSampler, v_in.uv); - //col.rgb += 0.1*uv.y; - float4 c; - c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); - col = mix(col, c, c.a); - c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); - col = mix(col, c, c.a); - - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] +[Alias('obs.powershell.websocket.SetMediaInputCursor')] +param( - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('mediaCursor')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$MediaCursor, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -20826,313 +14764,344 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFireworksShader { +function Set-OBSOutputSettings { -[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] +[Alias('obs.powershell.websocket.SetOutputSettings')] param( -# Set the show_flash of OBSFireworksShader -[Alias('show_flash')] -[ComponentModel.DefaultBindingProperty('show_flash')] -[Management.Automation.SwitchParameter] -$ShowFlash, -# Set the show_stars of OBSFireworksShader -[Alias('show_stars')] -[ComponentModel.DefaultBindingProperty('show_stars')] -[Management.Automation.SwitchParameter] -$ShowStars, -# Set the use_transparancy of OBSFireworksShader -[Alias('use_transparancy')] -[ComponentModel.DefaultBindingProperty('use_transparancy')] -[Management.Automation.SwitchParameter] -$UseTransparancy, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputSettings')] +[PSObject] +$OutputSettings, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fireworks' -$ShaderNoun = 'OBSFireworksShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#ifndef OPENGL -#define mat2 float2x2 -#define fract frac -#define mix lerp -#endif -uniform bool show_flash = true; -uniform bool show_stars = true; -uniform bool use_transparancy = true; -float distLine(float2 p, float2 a, float2 b) { - float2 pa = p - a; - float2 ba = b - a; - float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * t); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float linef(float2 uv, float2 a, float2 b, float w) { - //return smoothstep(w, w - 0.01, distLine(uv, a, b)); - return w / distLine(uv, a, b); -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -float N21(float2 p) { - p = fract(p * float2(233.34, 851.73)); - p += dot(p, p + 23.45); - return fract(p.x * p.y); -} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float2 N22(float2 p) { - float n = N21(p); - return float2(n, N21(p + n)); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float N11(float n) { - return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float particle(float2 uv, float2 p, float2 v, float r, float t) { - float g = -9.81; - float x = p.x + v.x * t; - float y = p.y + v.y * t + g / 2.0 * t * t; - float2 j = (float2(x, y) - uv) * 20.0; - float sparkle = 1.0 / dot(j, j); - return sparkle; } -float2 p1(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); -} -float2 p2(float2 p, float h, float t) { - return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); -} +} -float endTime(float h) { - return pow(h, 1.0 / 5.0) * 1.1; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSPersistentData { -float explosion(float2 uv, float2 p, float s, float n, float f, float t) { - float m = 0.0; - float dt = 0.5; - float seed2 = 0.32; - for(float i = 0.0; i < n; i++) { - seed2 += i; - float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); - float2 v = float2(cos(seed2), sin(seed2)) + rand; - m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); - } - return m; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] +[Alias('obs.powershell.websocket.SetPersistentData')] +param( -float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { - float2 p1v = p1(p, h, t); - float e = endTime(h); - return explosion(uv, p1v, s, n, f, t - e * 0.9); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('realm')] +[string] +$Realm, -float shaft(float2 uv, float2 p, float w, float h, float t) { - float2 p1v = p1(p, h, t) + float2(0.0, 0.3); - float2 p2v = p2(p, h, t); - float e = 1.0 / 0.95 * endTime(h); - float2 j = (p1v - uv) * 15.0; - float sparkle = 1.0 / dot(j, j); - return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotName')] +[string] +$SlotName, -float3 base(float2 uv) { - return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('slotValue')] +[PSObject] +$SlotValue, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float back(float2 uv, float2 p, float t) { - float dt = 0.3; - float j = length(p - uv); - float m = exp(-0.005 * j * j); - return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); -} -float stars(float2 uv) { - float r = N21(uv); - return smoothstep(0.001, 0.0, r); -} +process { -float mod(float x, float y) -{ - return x - y * floor(x / y); -} -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv - float2(0.5,0.5); - uv.y = uv.y * -1; - float t = elapsed_time / 10.0; - float scale = 10.0; - uv *= scale; - // - float4 col = image.Sample(textureSampler, v_in.uv); - if(show_stars){ - float c = stars(uv); - if(use_transparancy){ - col += float4(c,c,c,c)*(1.0-col.a); - }else{ - col += float4(c,c,c,c);//*(1.0-orig_col.a); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - - } - - float a = -0.035 * sin(t * 15.0); - float co = cos(a); - float si = sin(a); - mat2 trans1 = mat2(float2(co, si), float2(-si, co)); - float2 trans2 = float2(-15.0 * a, 0.0); -#ifndef OPENGL - uv = mul(uv, trans1); -#else - uv *= trans1; -#endif - uv += trans2; - - for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { - float ti = mod(t * 9.0 - i * 5.0, 4.0); - float scale = mix(2.0, 0.3, ti / 4.0); - float2 uvs = uv * scale; - float rand = N11(i); - float h = 10.0 + rand * 4.0; - float w = 0.02; - float n = 80.0; - float s = 0.9; - float f = 1.5; - float2 p = float2(mix(-8.0, 8.0, rand), -10.0); - float fw = fireworks(uvs, p, h, n, s, f, ti); - float3 bc = base(uv); - col += float4(bc*fw, fw); - col += shaft(uvs, p, w, h, ti); - if(show_flash){ - if(use_transparancy){ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; - }else{ - col += back(uvs, float2(p.x, p.y + h), ti - 1.8); + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - } - - return col; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSProfileParameter { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] +[Alias('obs.powershell.websocket.SetProfileParameter')] +param( + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterCategory')] +[string] +$ParameterCategory, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterName')] +[string] +$ParameterName, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('parameterValue')] +[string] +$ParameterValue, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21141,188 +15110,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeShader { +function Set-OBSRecordDirectory { -[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] +[Alias('obs.powershell.websocket.SetRecordDirectory')] param( -# Set the center_x_percent of OBSFisheyeShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Single] -$CenterYPercent, -# Set the power of OBSFisheyeShader -[ComponentModel.DefaultBindingProperty('power')] -[Single] -$Power, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('recordDirectory')] +[string] +$RecordDirectory, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fisheye' -$ShaderNoun = 'OBSFisheyeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); - } else if(power <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); - } - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21331,213 +15217,121 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFisheyeXyShader { +function Set-OBSSceneItemBlendMode { -[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] +[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] param( -# Set the center_x_percent of OBSFisheyeXyShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Single] -$CenterXPercent, -# Set the center_y_percent of OBSFisheyeXyShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Single] -$CenterYPercent, -# Set the power_x of OBSFisheyeXyShader -[Alias('power_x')] -[ComponentModel.DefaultBindingProperty('power_x')] -[Single] -$PowerX, -# Set the power_y of OBSFisheyeXyShader -[Alias('power_y')] -[ComponentModel.DefaultBindingProperty('power_y')] -[Single] -$PowerY, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] +[string] +$SceneItemBlendMode, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'fisheye-xy' -$ShaderNoun = 'OBSFisheyeXyShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float center_x_percent< - string label = "Center x percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float center_y_percent< - string label = "Center y percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 50.0; -uniform float power_x< - string label = "Power x"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; -uniform float power_y< - string label = "Power y"; - string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.75; -float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - if (power_x >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; - } else if(power_x <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; - } - if (power_y >= 0.0001){ - float b = sqrt(dot(center_pos, center_pos)); - uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; - } else if(power_y <= -0.0001){ - float b; - if (uv_pixel_interval.x < uv_pixel_interval.y){ - b = center_pos.x; - } else { - b = center_pos.y; - } - uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; - } - return image.Sample(textureSampler, uv); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21546,163 +15340,121 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFlipShader { +function Set-OBSSceneItemEnabled { -[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] +[Alias('obs.powershell.websocket.SetSceneItemEnabled')] param( -# Set the Horizontal of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSFlipShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] +[switch] +$SceneItemEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'Flip' -$ShaderNoun = 'OBSFlipShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// A Simple Flip Shader - -uniform bool Horizontal< - string label = "Flip horizontally"; -> = true; -uniform bool Vertical< - string label = "Flip vertically"; -> = true; -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - if (Horizontal == true) { - pos.x = 1 - pos.x; - } - if (Vertical == true) { - pos.y = 1 - pos.y; - } - - return image.Sample(textureSampler, pos); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21711,238 +15463,122 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSFrostedGlassShader { +function Set-OBSSceneItemIndex { -[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] +[Alias('obs.powershell.websocket.SetSceneItemIndex')] param( -# Set the Alpha_Percent of OBSFrostedGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Amount of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Amount')] -[Single] -$Amount, -# Set the Scale of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Scale')] -[Single] -$Scale, -# Set the Animate of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('Animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the Horizontal_Border of OBSFrostedGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSFrostedGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] -[Single] -$BorderOffset, -# Set the Border_Color of OBSFrostedGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the notes of OBSFrostedGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'frosted_glass' -$ShaderNoun = 'OBSFrostedGlassShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter - -uniform float Alpha_Percent< - string label = "Alpha Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Amount< - string label = "Amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.03; -uniform float Scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 5.1; -uniform bool Animate; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.1; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform string notes< - string widget_type = "info"; -> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; - -float rand(float2 co) -{ - float scale = Scale; - if (Animate) - scale *= rand_f; - float2 v1 = float2(92.0,80.0); - float2 v2 = float2(41.0,62.0); - return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - float3 tc = rgba.rgb * Border_Color.rgb; - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; - if (uv_compare < (Border_Offset - 0.005)) - { - float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); - tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - tc = image.Sample(textureSampler, v_in.uv).rgb; - } - return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -21951,450 +15587,244 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGammaCorrectionShader { +function Set-OBSSceneItemLocked { -[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] +[Alias('obs.powershell.websocket.SetSceneItemLocked')] param( -# Set the Red of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Red')] -[Single] -$Red, -# Set the Green of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Green')] -[Single] -$Green, -# Set the Blue of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('Blue')] -[Single] -$Blue, -# Set the notes of OBSGammaCorrectionShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemLocked')] +[switch] +$SceneItemLocked, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gamma_correction' -$ShaderNoun = 'OBSGammaCorrectionShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -uniform float Red< - string label = "Red"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform float Green< - string label = "Green"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform float Blue< - string label = "Blue"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; -uniform string notes< - string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction." -float4 mainImage(VertData v_in) : TARGET -{ - float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); - float4 c = image.Sample(textureSampler, v_in.uv); - c.rgb = pow(c.rgb, 1.0 / gammaRGB); - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } +} - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneItemTransform { - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } - } -} -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] +[Alias('obs.powershell.websocket.SetSceneItemTransform')] +param( +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurAdvancedShader { +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemId')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$SceneItemId, -[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] -param( -# Set the Directions of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Directions')] -[Single] -$Directions, -# Set the Quality of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Quality')] -[Single] -$Quality, -# Set the Size of OBSGaussianBlurAdvancedShader -[ComponentModel.DefaultBindingProperty('Size')] -[Single] -$Size, -# Set the Mask_Left of OBSGaussianBlurAdvancedShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurAdvancedShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurAdvancedShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneItemTransform')] +[PSObject] +$SceneItemTransform, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur-advanced' -$ShaderNoun = 'OBSGaussianBlurAdvancedShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float Directions< - string label = "Directions (16.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) -uniform float Quality< - string label = "Quality (4.0)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 100.0; - float step = 1.0; -> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) -uniform float Size< - string label = "Size (8.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 8.0; // BLUR SIZE (Radius) -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; - -float4 mainImage(VertData v_in) : TARGET -{ - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; - - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; - c.a = transparent / count; - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22403,342 +15833,238 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurShader { +function Set-OBSSceneName { -[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] +[Alias('obs.powershell.websocket.SetSceneName')] param( -# Set the ViewProj of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the imageSize of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageSize')] -[Single[]] -$ImageSize, -# Set the imageTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('imageTexel')] -[Single[]] -$ImageTexel, -# Set the u_radius of OBSGaussianBlurShader -[Alias('u_radius')] -[ComponentModel.DefaultBindingProperty('u_radius')] -[Int32] -$URadius, -# Set the u_diameter of OBSGaussianBlurShader -[Alias('u_diameter')] -[ComponentModel.DefaultBindingProperty('u_diameter')] -[Int32] -$UDiameter, -# Set the u_texelDelta of OBSGaussianBlurShader -[Alias('u_texelDelta')] -[ComponentModel.DefaultBindingProperty('u_texelDelta')] -[Single[]] -$UTexelDelta, -# Set the elapsed_time of OBSGaussianBlurShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianBlurShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianBlurShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianBlurShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the kernel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernel')] -[String] -$Kernel, -# Set the kernelTexel of OBSGaussianBlurShader -[ComponentModel.DefaultBindingProperty('kernelTexel')] -[Single[]] -$KernelTexel, -# Set the pixel_size of OBSGaussianBlurShader -[Alias('pixel_size')] -[ComponentModel.DefaultBindingProperty('pixel_size')] -[Single] -$PixelSize, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newSceneName')] +[string] +$NewSceneName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur' -$ShaderNoun = 'OBSGaussianBlurShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 -// OBS Default -uniform float4x4 ViewProj; - -// Settings (Shared) -uniform texture2d image; -uniform float2 imageSize; -uniform float2 imageTexel; -uniform int u_radius; -uniform int u_diameter; -uniform float2 u_texelDelta; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -// Settings (Private) -//uniform float registerkernel[25]; -uniform texture2d kernel; -uniform float2 kernelTexel; -uniform float pixel_size = 1.0; -sampler_state pointClampSampler { - Filter = Point; - AddressU = Clamp; - AddressV = Clamp; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -sampler_state bilinearClampSampler { - Filter = Bilinear; - AddressU = Clamp; - AddressV = Clamp; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float Gaussian(float x, float o) -{ - float pivalue = 3.1415926535897932384626433832795; - return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); -} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -VertData VSDefault(VertData vert_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = vert_in.uv; - return vert_out; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel) - { - float l_gauss = Gaussian(0.0, 1.0); - float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_gauss += l_g; - } - l_value = l_value * (1.0 / l_gauss); - return l_value; } -float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = float2(0, 0); - for (int k = 1; k <= p_radius; k++) { - l_uvoffset += p_uvStep; - float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - } - return l_value; -} -/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, - texture2d p_image, float2 p_imageTexel, - texture2d p_kernel, float2 p_kernelTexel) - { - if (pixel_size % 2 == 0) { - float4 l_value = image.Sample(pointClampSampler, p_uv) - * kernel.Sample(pointClampSampler, float2(0, 0)).r; - float2 l_uvoffset = p_texel; - float2 l_koffset = p_kernelTexel; - for (int k = 1; k <= pixel_size; k++) { - float l_g = kernel.Sample(pointClampSampler, l_koffset).r; - float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; - float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; - l_value += l_p + l_n; - l_uvoffset += p_texel; - l_koffset += p_kernelTexel; - } - return l_value; - } else { - return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) - } -}*/ +} -float4 PSGaussian(VertData vert_in) : TARGET -{ - - float4 color = image.Sample(pointClampSampler, vert_in.uv); + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSceneSceneTransitionOverride { - float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] +[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] +param( - /* - return InternalGaussianPrecalculated( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneName')] +[string] +$SceneName, - /* - return InternalGaussianPrecalculatedNVOptimize( - vert_in.uv, u_texelDelta, u_radius, - image, imageTexel, - kernel, kernelTexel); - */ -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sceneUuid')] +[string] +$SceneUuid, -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSGaussian(vert_in); - } -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionName')] +[string] +$TransitionName, -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('transitionDuration')] +[ValidateRange(50,20000)] +[double] +$TransitionDuration, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +process { - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22747,249 +16073,120 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianBlurSimpleShader { +function Set-OBSSourceFilterEnabled { -[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] +[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] param( -# Set the Strength of OBSGaussianBlurSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Int32] -$Strength, -# Set the Mask_Left of OBSGaussianBlurSimpleShader -[Alias('Mask_Left')] -[ComponentModel.DefaultBindingProperty('Mask_Left')] -[Single] -$MaskLeft, -# Set the Mask_Right of OBSGaussianBlurSimpleShader -[Alias('Mask_Right')] -[ComponentModel.DefaultBindingProperty('Mask_Right')] -[Single] -$MaskRight, -# Set the Mask_Top of OBSGaussianBlurSimpleShader -[Alias('Mask_Top')] -[ComponentModel.DefaultBindingProperty('Mask_Top')] -[Single] -$MaskTop, -# Set the Mask_Bottom of OBSGaussianBlurSimpleShader -[Alias('Mask_Bottom')] -[ComponentModel.DefaultBindingProperty('Mask_Bottom')] -[Single] -$MaskBottom, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterEnabled')] +[switch] +$FilterEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-blur-simple' -$ShaderNoun = 'OBSGaussianBlurSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int Strength< - string label = "Strength (1)"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 1.0; -uniform float Mask_Left< - string label = "Mask left (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Right< - string label = "Mask right (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Top< - string label = "Mask top (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float Mask_Bottom< - string label = "Mask bottom (1.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -float4 mainImage(VertData v_in) : TARGET -{ - if(Strength <= 0) - return image.Sample(textureSampler, v_in.uv); - - if(Mask_Left + Mask_Right > 1.0){ - if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ - return image.Sample(textureSampler, v_in.uv); - } - }else{ - if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ - return image.Sample(textureSampler, v_in.uv); - } - } - if(Mask_Top + Mask_Bottom > 1.0){ - if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ - return image.Sample(textureSampler, v_in.uv); - } - }else { - if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ - return image.Sample(textureSampler, v_in.uv); - } - } - - float Pi = 6.28318530718; // Pi*2 - - float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) - float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) - - float4 c = image.Sample(textureSampler, v_in.uv); - float4 oc = c; - float transparent = oc.a; - int count = 1; - float samples = oc.a; - - // Blur calculations - [loop] for( float d=0.0; d 0.0) - c /= samples; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - c.a = transparent / count; - return c; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -22998,378 +16195,243 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianExampleShader { +function Set-OBSSourceFilterIndex { -[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] +[Alias('obs.powershell.websocket.SetSourceFilterIndex')] param( -# Set the ViewProj of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianExampleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianExampleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianExampleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSGaussianExampleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSGaussianExampleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the initial_image of OBSGaussianExampleShader -[Alias('initial_image')] -[ComponentModel.DefaultBindingProperty('initial_image')] -[String] -$InitialImage, -# Set the before_image of OBSGaussianExampleShader -[Alias('before_image')] -[ComponentModel.DefaultBindingProperty('before_image')] -[String] -$BeforeImage, -# Set the after_image of OBSGaussianExampleShader -[Alias('after_image')] -[ComponentModel.DefaultBindingProperty('after_image')] -[String] -$AfterImage, -# Set the text_color of OBSGaussianExampleShader -[Alias('text_color')] -[ComponentModel.DefaultBindingProperty('text_color')] -[String] -$TextColor, -# Set the max_distance of OBSGaussianExampleShader -[Alias('max_distance')] -[ComponentModel.DefaultBindingProperty('max_distance')] -[Single] -$MaxDistance, -# Set the exp of OBSGaussianExampleShader -[ComponentModel.DefaultBindingProperty('exp')] -[Single] -$Exp, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterIndex')] +[ValidateRange(0,[int]::MaxValue)] +[double] +$FilterIndex, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-example' -$ShaderNoun = 'OBSGaussianExampleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; - -/*-------------------------. -| :: Texture and sampler:: | -''-------------------------*/ -uniform texture2d initial_image; -sampler_state initial_sampler -{ - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = initial_image; -}; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform texture2d before_image; -sampler_state before_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = before_image; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform texture2d after_image; -sampler_state after_sampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; - texture2d = after_image; -}; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -struct ColorData { - float4 initial_color : SV_TARGET0; - float4 before_color: SV_TARGET1; - float4 after_color : SV_TARGET2; -}; +} -uniform float4 text_color; -uniform float max_distance; -uniform float exp; -#define PI 3.141592653589793238462643383279502884197169399375105820974 +} -VertData mainTransform(VertData v_in) -{ - VertData vert_out = v_in; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSourceFilterName { -float4 grayscale(float4 color) -{ - float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; - return float4(grayscale, grayscale, grayscale, color.a); -} -float4 gaussian(VertData v_in, float angle) -{ - float rad = radians(angle); - float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); - float2 dir_2 = dir * 2.0; - float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; - - float4 px_away = image.Sample(textureSampler, v_in.uv + dir); - px_away += image.Sample(textureSampler, v_in.uv - dir); - px_away *= 0.25; - - float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); - px_2_away *= 0.0625; - - return ret + px_away + px_2_away; -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] +[Alias('obs.powershell.websocket.SetSourceFilterName')] +param( -ColorData setColorData(VertData v_in): SV_TARGET0 -{ - //string RenderTarget0 = "initial_image"; - ColorData cd;// = (ColorData)0; - cd.initial_color = image.Sample(textureSampler, v_in.uv); - cd.before_color = float4(0.0,0.0,1.0,1.0); - cd.after_color = float4(1.0,0.0,0.0,1.0); - return cd; -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] +$SourceName, -float4 blurImageH(VertData v_in) : SV_TARGET1 -{ - //string RenderTarget1 = "before_image"; - //ColorData cd = (ColorData)0; - //cd.initial_color = image.Sample(textureSampler, v_in.uv); - //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); - return float4(0.0,0.0,1.0,1.0); -} +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, -float4 blurImageV(VertData v_in) : SV_TARGET2 -{ - //string RenderTarget2 = "after_image"; - //ColorData cd = (ColorData)0; - //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); - return float4(1.0,0.0,0.0,1.0); -} +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] +$FilterName, -float4 mainImage(VertData v_in) : SV_TARGET0 -{ - float4 color; - ColorData cd;// = (ColorData)0; - - //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); - //cd.before_color = before_image.Sample(before_sampler, v_in.uv); - cd.after_color = after_image.Sample(before_sampler, v_in.uv); - - if (max_distance <= 5) { - color = cd.before_color; - } - else { - color = cd.after_color;//image.Sample(textureSampler, v_in.uv); - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('newFilterName')] +[string] +$NewFilterName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float4 gray = grayscale(color); - float4 gray_text = grayscale(text_color); - float d = distance(gray.rgb, gray_text.rgb); - if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ - float d_c = pow(d*2, exp) / pow(2, exp); - d_c = sin(d_c * PI / 2); - d = pow(1.0 - sin(d * PI/4), exp); - color.rgb = float3(d,d,d); - } +process { - return color; -} -technique Draw -{ - pass pre - { - vertex_shader = mainTransform(v_in); - pixel_shader = setColorData(v_in); - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - pass b0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageH(v_in); - } + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - pass b1 - { - vertex_shader = mainTransform(v_in); - pixel_shader = blurImageV(v_in); - } - - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } - -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -23378,290 +16440,125 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGaussianSimpleShader { +function Set-OBSSourceFilterSettings { -[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] +[Alias('obs.powershell.websocket.SetSourceFilterSettings')] param( -# Set the ViewProj of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSGaussianSimpleShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSGaussianSimpleShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSGaussianSimpleShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSGaussianSimpleShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSGaussianSimpleShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSGaussianSimpleShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the rand_instance_f of OBSGaussianSimpleShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] -[Single] -$RandInstanceF, -# Set the rand_activation_f of OBSGaussianSimpleShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] -[Single] -$RandActivationF, -# Set the loops of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSGaussianSimpleShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the samples of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('samples')] -[Int32] -$Samples, -# Set the LOD of OBSGaussianSimpleShader -[ComponentModel.DefaultBindingProperty('LOD')] -[Int32] -$LOD, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] +[ComponentModel.DefaultBindingProperty('sourceName')] +[string] $SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + [Parameter(ValueFromPipelineByPropertyName)] -[String] +[ComponentModel.DefaultBindingProperty('sourceUuid')] +[string] +$SourceUuid, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterName')] +[string] $FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('filterSettings')] +[PSObject] +$FilterSettings, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('overlay')] +[switch] +$Overlay, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gaussian-simple' -$ShaderNoun = 'OBSGaussianSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/ltScRG Converted inspiration - -//Section to converting GLSL to HLSL - can delete -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float - -/* -**Shaders have these variables pre loaded by the plugin** -**this section can be deleted** - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; -*/ - -// 16x acceleration of https://www.shadertoy.com/view/4tSyzy -// by applying gaussian at intermediate MIPmap level. - -uniform int samples< - string label = "Samples"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 25; - int step = 1; -> = 16; -uniform int LOD< - string label = "LOD"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 25; - int step = 1; -> = 2; // gaussian done on MIPmap at scale LOD - -float gaussian(vec2 i) -{ - float sigma = (float(samples) * .25); - return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); -} - -vec4 blur(vec2 U, vec2 scale) -{ - vec4 O = vec4(0,0,0,0); - int sLOD = (1 << LOD); // tile size = 2^LOD - int s = samples / sLOD; - - for (int i = 0; i < s * s; i++) - { - vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; - O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); - //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); - //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); - } - - return O / O.a; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; - //float2 iResolution = 1 - v_in.uv + 1.0; - //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); - return blur(v_in.uv / iResolution, 1.0 / iResolution); - //return rgba; -} - - - + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -23670,335 +16567,217 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGbCameraShader { +function Set-OBSStreamServiceSettings { -[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] +[Alias('obs.powershell.websocket.SetStreamServiceSettings')] param( -# Set the pixelSize of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('pixelSize')] -[Single] -$PixelSize, -# Set the dither_factor of OBSGbCameraShader -[Alias('dither_factor')] -[ComponentModel.DefaultBindingProperty('dither_factor')] -[Single] -$DitherFactor, -# Set the alternative_bayer of OBSGbCameraShader -[Alias('alternative_bayer')] -[ComponentModel.DefaultBindingProperty('alternative_bayer')] -[Management.Automation.SwitchParameter] -$AlternativeBayer, -# Set the brightness of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('brightness')] -[Single] -$Brightness, -# Set the contrast of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('contrast')] -[Single] -$Contrast, -# Set the gamma of OBSGbCameraShader -[ComponentModel.DefaultBindingProperty('gamma')] -[Single] -$Gamma, -# Set the color_1 of OBSGbCameraShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the color_2 of OBSGbCameraShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the color_3 of OBSGbCameraShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the color_4 of OBSGbCameraShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] -[String] -$Color4, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceType')] +[string] +$StreamServiceType, + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('streamServiceSettings')] +[PSObject] +$StreamServiceSettings, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gb-camera' -$ShaderNoun = 'OBSGbCameraShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * ------------------------------------------------------------ - * "THE BEERWARE LICENSE" (Revision 42): - * maple wrote this code. As long as you retain this - * notice, you can do whatever you want with this stuff. If we - * meet someday, and you think this stuff is worth it, you can - * buy me a beer in return. - * ------------------------------------------------------------ - * from https://www.shadertoy.com/view/3tSXRh - * adopted for OBS by Exeldro - * ------------------------------------------------------------ - */ -uniform float pixelSize< - string label = "Pixel Size"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 50.0; - float step = 0.1; -> = 3.0; -uniform float dither_factor< - string label = "Dither Factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform bool alternative_bayer; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -uniform float brightness< - string label = "Brightness"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float contrast< - string label = "Contrast"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float gamma< - string label = "Gamma"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.6; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; -uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; -uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; -uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -// quantize coords to low resolution -float2 pixelize(float2 uv, float2 pixelSize) { - float2 factor = pixelSize / uv_size; - return floor(uv / factor) * factor; -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -float3 colorLUT(float3 color) { - float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; - if(gray < 0.25) - return color_1.rgb; - if(gray < 0.50) - return color_2.rgb; - if(gray < 0.75) - return color_3.rgb; - return color_4.rgb; } -// adjust brightness, contrast and gamma levels of a color -float3 levels(float3 color, float brightness, float contrast, float3 gamma) { - float3 value = (color - 0.5) * contrast + 0.5; - value = clamp(value + brightness, 0.0, 1.0); - return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); -} -float3 levels(float3 color, float brightness, float contrast, float gamma) { - return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); -} -// applies the dithering filter to a color map -float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { - // reduces pixel space to the selected pixel size - float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); - - // applies the bayer matrix filter to the color map - pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); - int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); - float bayer; - if (alternative_bayer){ -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ - 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ - 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ - 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ - 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ - 51, 19, 59, 27, 49, 17, 57, 25, - 15, 47, 7, 39, 13, 45, 5, 37, - 63, 31, 55, 23, 61, 29, 53, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; - } else { -#ifdef OPENGL - const int[64] bayer8 = int[64]( -#else - const int bayer8[64] = { -#endif - 0, 48, 12, 60, 3, 51, 15, 63, - 32, 16, 44, 28, 35, 19, 47, 31, - 8, 56, 4, 52, 11, 59, 7, 55, - 40, 24, 36, 20, 43, 27, 39, 23, - 2, 50, 14, 62, 1, 49, 13, 61, - 34, 18, 46, 30, 33, 17, 45, 29, - 10, 58, 6, 54, 9, 57, 5, 53, - 42, 26, 38, 22, 41, 25, 37, 21 -#ifdef OPENGL - ); -#else - }; -#endif - bayer = (bayer8[index]-31.0)/32.0; - } - float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); - // limits it to the selected palette - color = colorLUT(bayerColor); +} - return color; -} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSStudioModeEnabled { -float4 mainImage(VertData v_in) : TARGET -{ - float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); - texcoord = clamp(texcoord, 0.001, 1.0); - float4 c = image.Sample(textureSampler, texcoord); - float3 color = c.rgb; - - color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); - - color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); - - return float4(color.r, color.g, color.b, c.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] +[Alias('obs.powershell.websocket.SetStudioModeEnabled')] +param( - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('studioModeEnabled')] +[switch] +$StudioModeEnabled, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24007,267 +16786,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlassShader { +function Set-OBSTBarPosition { -[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] +[Alias('obs.powershell.websocket.SetTBarPosition')] param( -# Set the Alpha_Percent of OBSGlassShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Single] -$AlphaPercent, -# Set the Offset_Amount of OBSGlassShader -[Alias('Offset_Amount')] -[ComponentModel.DefaultBindingProperty('Offset_Amount')] -[Single] -$OffsetAmount, -# Set the xSize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('xSize')] -[Int32] -$XSize, -# Set the ySize of OBSGlassShader -[ComponentModel.DefaultBindingProperty('ySize')] -[Int32] -$YSize, -# Set the Reflection_Offset of OBSGlassShader -[Alias('Reflection_Offset')] -[ComponentModel.DefaultBindingProperty('Reflection_Offset')] -[Int32] -$ReflectionOffset, -# Set the Horizontal_Border of OBSGlassShader -[Alias('Horizontal_Border')] -[ComponentModel.DefaultBindingProperty('Horizontal_Border')] -[Management.Automation.SwitchParameter] -$HorizontalBorder, -# Set the Border_Offset of OBSGlassShader -[Alias('Border_Offset')] -[ComponentModel.DefaultBindingProperty('Border_Offset')] -[Single] -$BorderOffset, -# Set the Border_Color of OBSGlassShader -[Alias('Border_Color')] -[ComponentModel.DefaultBindingProperty('Border_Color')] -[String] -$BorderColor, -# Set the Glass_Color of OBSGlassShader -[Alias('Glass_Color')] -[ComponentModel.DefaultBindingProperty('Glass_Color')] -[String] -$GlassColor, -# Set the notes of OBSGlassShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('position')] +[ValidateRange(0,1)] +[double] +$Position, + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('release')] +[switch] +$Release, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glass' -$ShaderNoun = 'OBSGlassShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 -uniform float Alpha_Percent< - string label = "Alpha Percent"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100.0; -uniform float Offset_Amount< - string label = "Offset Amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.8; -uniform int xSize< - string label = "x Size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int ySize< - string label = "y Size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 8; -uniform int Reflection_Offset< - string label = "Reflection Offset"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 2; -uniform bool Horizontal_Border; -uniform float Border_Offset< - string label = "Border Offset"; - string widget_type = "slider"; - float minimum = -0.01; - float maximum = 1.01; - float step = 0.01; -> = 0.5; -uniform float4 Border_Color = {.8,.5,1.0,1.0}; -uniform float4 Glass_Color; -uniform string notes< - string widget_type = "info"; -> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; - -float mod(float a, float b){ - float d = a / b; - return (d-floor(d))*b; -} - -float4 mainImage(VertData v_in) : TARGET -{ - - - int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); - int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); - float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); - float2 uv = v_in.uv + offsets; - float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); - - float4 rgba = image.Sample(textureSampler, v_in.uv); - float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); - rgba = image.Sample(textureSampler, uv); - float4 rgba_glass = image.Sample(textureSampler, uv2); - - float uv_compare = v_in.uv.x; - if (Horizontal_Border) - uv_compare = v_in.uv.y; - if (uv_compare < (Border_Offset - 0.005)) - { - rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; - } - else if (uv_compare >= (Border_Offset + 0.005)) - { - rgba_output = image.Sample(textureSampler, v_in.uv); - } - return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24276,352 +16899,136 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchAnalogShader { +function Set-OBSVideoSettings { -[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] +[Alias('obs.powershell.websocket.SetVideoSettings')] param( -# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader -[Alias('scan_line_jitter_displacement')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] -[Single] -$ScanLineJitterDisplacement, -# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader -[Alias('scan_line_jitter_threshold_percent')] -[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] -[Int32] -$ScanLineJitterThresholdPercent, -# Set the vertical_jump_amount of OBSGlitchAnalogShader -[Alias('vertical_jump_amount')] -[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] -[Single] -$VerticalJumpAmount, -# Set the vertical_speed of OBSGlitchAnalogShader -[Alias('vertical_speed')] -[ComponentModel.DefaultBindingProperty('vertical_speed')] -[Single] -$VerticalSpeed, -# Set the horizontal_shake of OBSGlitchAnalogShader -[Alias('horizontal_shake')] -[ComponentModel.DefaultBindingProperty('horizontal_shake')] -[Single] -$HorizontalShake, -# Set the color_drift_amount of OBSGlitchAnalogShader -[Alias('color_drift_amount')] -[ComponentModel.DefaultBindingProperty('color_drift_amount')] -[Single] -$ColorDriftAmount, -# Set the color_drift_speed of OBSGlitchAnalogShader -[Alias('color_drift_speed')] -[ComponentModel.DefaultBindingProperty('color_drift_speed')] -[Single] -$ColorDriftSpeed, -# Set the pulse_speed_percent of OBSGlitchAnalogShader -[Alias('pulse_speed_percent')] -[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] -[Int32] -$PulseSpeedPercent, -# Set the alpha_percent of OBSGlitchAnalogShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] -[Int32] -$AlphaPercent, -# Set the rotate_colors of OBSGlitchAnalogShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Replace_Image_Color of OBSGlitchAnalogShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGlitchAnalogShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the notes of OBSGlitchAnalogShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('fpsNumerator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsNumerator, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('fpsDenominator')] +[ValidateRange(1,[int]::MaxValue)] +[double] +$FpsDenominator, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseWidth')] +[ValidateRange(1,4096)] +[double] +$BaseWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('baseHeight')] +[ValidateRange(1,4096)] +[double] +$BaseHeight, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputWidth')] +[ValidateRange(1,4096)] +[double] +$OutputWidth, + +[Parameter(ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputHeight')] +[ValidateRange(1,4096)] +[double] +$OutputHeight, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glitch_analog' -$ShaderNoun = 'OBSGlitchAnalogShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float scan_line_jitter_displacement< - string label = "Scan line jitter"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 33.0; // (displacement, threshold) -uniform int scan_line_jitter_threshold_percent< - string label = "scan line jitter threshold percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 95; -uniform float vertical_jump_amount< - string label = "Vertical jump amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float vertical_speed< - string label = "Vertical speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform float horizontal_shake< - string label = "Horizontal shake"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float color_drift_amount< - string label = "Color drift amount"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->; -uniform float color_drift_speed< - string label = "Color drift speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; ->;// (amount, speed) -uniform int pulse_speed_percent< - string label = "Pulse speed percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int alpha_percent< - string label = "Aplha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string notes< - string widget_type = "info"; -> ="play with settings!"; -float nrand(float x, float y) -{ - float value = dot(float2(x, y), float2(12.9898 , 78.233 )); - return frac(sin(value) * 43758.5453); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float speed = pulse_speed_percent * 0.01; - float alpha = alpha_percent * 0.01; - float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; - float u = v_in.uv.x; - float v = v_in.uv.y; - float t = sin(elapsed_time * speed) * 2 - 1; - float4 rgba = image.Sample(textureSampler, v_in.uv); - - // Scan line jitter - float jitter = nrand(v, t) * 2 - 1; - jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; - - // Vertical jump - float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); - - // Horizontal shake - float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; - - //// Color drift - float drift = sin(jump + color_drift_speed) * color_drift_amount; - - float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); - float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); - - if(rotate_colors) - { - // get general time number between 0 and 4 - float tx = (t + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls - //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 - src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); - //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 - src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); - //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 - src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); - - } - - float4 color = rgba; - float4 original_color = color; - rgba = float4(src1.x, src2.x, src1.y, alpha); - - if (Apply_To_Alpha_Layer) - { - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color, alpha); - } - - if (Apply_To_Specific_Color) - { - color = original_color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, alpha); - } - - return rgba; -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24630,243 +17037,105 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchPeriodicShader { +function Start-OBSOutput { -[Alias('Set-OBSGlitchPeriodicShader','Add-OBSGlitchPeriodicShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] +[Alias('obs.powershell.websocket.StartOutput')] param( -# Set the PERI of OBSGlitchPeriodicShader -[ComponentModel.DefaultBindingProperty('PERI')] -[Single] -$PERI, -# Set the DURA of OBSGlitchPeriodicShader -[ComponentModel.DefaultBindingProperty('DURA')] -[Single] -$DURA, -# Set the AMPL of OBSGlitchPeriodicShader -[ComponentModel.DefaultBindingProperty('AMPL')] -[Single] -$AMPL, -# Set the SCRA of OBSGlitchPeriodicShader -[ComponentModel.DefaultBindingProperty('SCRA')] -[Single] -$SCRA, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glitch-periodic' -$ShaderNoun = 'OBSGlitchPeriodicShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Created by Éric Nicolas (ccjmne) for use with obs-shaderfilter 12/2025 -// Port of: https://www.shadertoy.com/view/WfVfDh -// Originally forked from: https://www.shadertoy.com/view/MtXBDs - -#define PI 3.14159265359 - -/* For visual explanation of the paramters, see */ -/* https://www.desmos.com/calculator/vezu1wyqma */ -/* */ -/* Period How often a glitch occurs (in seconds) 0–? */ -/* Duration How long a glitch lasts (in seconds) 0–Period */ -/* Amplitude How intense a glitch is 0–1 */ -/* Scratchiness How jittery a glitch is 0–1 */ - -uniform float PERI< - string label = "Period"; - string widget_type = "slider"; - float minimum = 1.; - float maximum = 60.; - float step = 1.; -> = 6.; - -uniform float DURA< - string label = "Duration"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 60.; - float step = .01; -> = .5; - -uniform float AMPL< - string label = "Amplitude"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 1.; - float step = .01; -> = .15; - -uniform float SCRA< - string label = "Scratchiness"; - string widget_type = "slider"; - float minimum = 0.; - float maximum = 1.; - float step = .01; -> = .2; - -float random2d(float2 n) { - return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); -} - -float randomRange(in float2 seed, in float lo, in float hi) { - return lo + random2d(seed) * (hi - lo); -} - -float insideRange(float v, float bottom, float top) { - return step(bottom, v) - step(top, v); -} - -float4 mainImage(VertData v_in): TARGET { - float time = floor(elapsed_time * SCRA * 60.); - float2 uv = v_in.uv; - // Periodic intermittence - float AMP = AMPL * (cos(2. * PI * max(0., (mod(-elapsed_time / PERI, 1.) - 1.) * PERI / DURA + 1.)) * -.5 + .5); - float4 outCol = image.Sample(textureSampler, uv); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - // Randomly offset slices horizontally - float offsetMax = AMP / 2.; - for (float i = 0.; i < 10. * AMP; i += 1.) { - float sliceY = random2d( float2(time, 2345. + i)); - float sliceH = random2d( float2(time, 9035. + i)) * .25; - float offsetH = randomRange(float2(time, 9625. + i), -offsetMax, offsetMax); - float2 uvOff = uv; - uvOff.x += offsetH; - if (insideRange(uv.y, sliceY, frac(sliceY + sliceH)) == 1.) { - outCol = image.Sample(textureSampler, uvOff); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - - // Slightly offset one entire channel - offsetMax = AMP / 6.; - float2 colOff = float2( - randomRange(float2(time, 9545.), -offsetMax, offsetMax), - randomRange(float2(time, 7205.), -offsetMax, offsetMax) - ); - float rnd = random2d(float2(time , 9545.)); - if (rnd < .33) outCol.r = image.Sample(textureSampler, uv + colOff).r; - else if (rnd < .66) outCol.g = image.Sample(textureSampler, uv + colOff).g; - else outCol.b = image.Sample(textureSampler, uv + colOff).b; - - return outCol; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -24875,213 +17144,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlitchShader { +function Start-OBSRecord { -[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] +[Alias('obs.powershell.websocket.StartRecord')] param( -# Set the AMT of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('AMT')] -[Single] -$AMT, -# Set the SPEED of OBSGlitchShader -[ComponentModel.DefaultBindingProperty('SPEED')] -[Single] -$SPEED, -# The name of the source. This must be provided when adding an item for the first time +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glitch' -$ShaderNoun = 'OBSGlitchShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/MtXBDs -//inputs -uniform float AMT< - string label = "AMT"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.2; //0 - 1 glitch amount -uniform float SPEED< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.6; //0 - 1 speed - -//2D (returns 0 - 1) -float random2d(float2 n) { - return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); -} - -float randomRange (in float2 seed, in float min, in float max) { - return min + random2d(seed) * (max - min); -} -// return 1 if v inside 1d range -float insideRange(float v, float bottom, float top) { - return step(bottom, v) - step(top, v); -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - -float4 mainImage(VertData v_in) : TARGET -{ - - float time = floor(elapsed_time * SPEED * 60.0); - float2 uv = v_in.uv; - - //copy orig - float4 outCol = image.Sample(textureSampler, uv); - - //randomly offset slices horizontally - float maxOffset = AMT/2.0; - for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { - float sliceY = random2d(float2(time , 2345.0 + float(i))); - float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; - float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); - float2 uvOff = uv; - uvOff.x += hOffset; - if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ - outCol = image.Sample(textureSampler, uvOff); + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - } - - //do slight offset on one entire channel - float maxColOffset = AMT/6.0; - float rnd = random2d(float2(time , 9545.0)); - float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), - randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); - if (rnd < 0.33){ - outCol.r = image.Sample(textureSampler, uv + colOffset).r; - - }else if (rnd < 0.66){ - outCol.g = image.Sample(textureSampler, uv + colOffset).g; - - } else{ - outCol.b = image.Sample(textureSampler, uv + colOffset).b; - } - - return outCol; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Start-OBSReplayBuffer { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] +[Alias('obs.powershell.websocket.StartReplayBuffer')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25090,260 +17348,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGlowShader { +function Start-OBSStream { -[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] +[Alias('obs.powershell.websocket.StartStream')] param( -# Set the glow_percent of OBSGlowShader -[Alias('glow_percent')] -[ComponentModel.DefaultBindingProperty('glow_percent')] -[Int32] -$GlowPercent, -# Set the blur of OBSGlowShader -[ComponentModel.DefaultBindingProperty('blur')] -[Int32] -$Blur, -# Set the min_brightness of OBSGlowShader -[Alias('min_brightness')] -[ComponentModel.DefaultBindingProperty('min_brightness')] -[Int32] -$MinBrightness, -# Set the max_brightness of OBSGlowShader -[Alias('max_brightness')] -[ComponentModel.DefaultBindingProperty('max_brightness')] -[Int32] -$MaxBrightness, -# Set the pulse_speed of OBSGlowShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] -[Int32] -$PulseSpeed, -# Set the ease of OBSGlowShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the notes of OBSGlowShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'glow' -$ShaderNoun = 'OBSGlowShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int glow_percent< - string label = "Glow percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 10; -uniform int blur< - string label = "Blur"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 1; -uniform int min_brightness< - string label = "Min brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 27; -uniform int max_brightness< - string label = "Max brightness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform string notes< - string widget_type = "info"; -> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; - - -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; -} - -float BlurStyler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; -} -float4 mainImage(VertData v_in) : TARGET -{ - float2 offsets[4]; - offsets[0] = float2(-0.1, 0.125); - offsets[1] = float2(-0.1, -0.125); - offsets[2] = float2(0.1, -0.125); - offsets[3] = float2(0.1, 0.125); - // convert input for vector math - float4 col = image.Sample(textureSampler, v_in.uv); - float blur_amount = float(blur) /100.0; - float glow_amount = float(glow_percent) * 0.01; - float speed = float(pulse_speed) * 0.01; - float luminance_floor = float(min_brightness) /100.0; - float luminance_ceiling = float(max_brightness) /100.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (col.a > 0.0) - { - //circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } - // simple glow calc - for (int n = 0; n < 4; n++) { - b = BlurStyler(t, 0, c, d, ease); - float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); - float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; - if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) - { - ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); - col += (ncolor * (glow_amount * b)); - } - } - } - return col; + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25352,418 +17450,207 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSGradientShader { +function Start-OBSVirtualCam { -[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] +[Alias('obs.powershell.websocket.StartVirtualCam')] param( -# Set the start_color of OBSGradientShader -[Alias('start_color')] -[ComponentModel.DefaultBindingProperty('start_color')] -[String] -$StartColor, -# Set the start_step of OBSGradientShader -[Alias('start_step')] -[ComponentModel.DefaultBindingProperty('start_step')] -[Single] -$StartStep, -# Set the middle_color of OBSGradientShader -[Alias('middle_color')] -[ComponentModel.DefaultBindingProperty('middle_color')] -[String] -$MiddleColor, -# Set the middle_step of OBSGradientShader -[Alias('middle_step')] -[ComponentModel.DefaultBindingProperty('middle_step')] -[Single] -$MiddleStep, -# Set the end_color of OBSGradientShader -[Alias('end_color')] -[ComponentModel.DefaultBindingProperty('end_color')] -[String] -$EndColor, -# Set the end_step of OBSGradientShader -[Alias('end_step')] -[ComponentModel.DefaultBindingProperty('end_step')] -[Single] -$EndStep, -# Set the alpha_percent of OBSGradientShader -[Alias('alpha_percent')] -[ComponentModel.DefaultBindingProperty('alpha_percent')] -[Int32] -$AlphaPercent, -# Set the pulse_speed of OBSGradientShader -[Alias('pulse_speed')] -[ComponentModel.DefaultBindingProperty('pulse_speed')] -[Int32] -$PulseSpeed, -# Set the ease of OBSGradientShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the rotate_colors of OBSGradientShader -[Alias('rotate_colors')] -[ComponentModel.DefaultBindingProperty('rotate_colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the Apply_To_Alpha_Layer of OBSGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the horizontal of OBSGradientShader -[ComponentModel.DefaultBindingProperty('horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the vertical of OBSGradientShader -[ComponentModel.DefaultBindingProperty('vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the gradient_center_width_percentage of OBSGradientShader -[Alias('gradient_center_width_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] -[Int32] -$GradientCenterWidthPercentage, -# Set the gradient_center_height_percentage of OBSGradientShader -[Alias('gradient_center_height_percentage')] -[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] -[Int32] -$GradientCenterHeightPercentage, -# Set the notes of OBSGradientShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'gradient' -$ShaderNoun = 'OBSGradientShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; -uniform float start_step< - string label = "Start step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.15; -uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; -uniform float middle_step< - string label = "Middle step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.4; -uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; -uniform float end_step< - string label = "End step"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.9; -uniform int alpha_percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform int pulse_speed< - string label = "Pulse speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform bool rotate_colors; -uniform bool Apply_To_Alpha_Layer = true; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform bool horizontal; -uniform bool vertical; -uniform int gradient_center_width_percentage< - string label = "gradient center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int gradient_center_height_percentage< - string label = "gradient center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; -float EaseInOutCircTimer(float t, float b, float c, float d) { - t /= d / 2; - if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; - t -= 2; - return c / 2 * (sqrt(1 - t * t) + 1) + b; -} -float BlurStyler(float t, float b, float c, float d, bool ease) -{ - if (ease) return EaseInOutCircTimer(t, 0, c, d); - return t; -} + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -struct gradient -{ - float4 color; - float step; -}; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.14159265f;//acos(-1); - float speed = float(pulse_speed) * 0.01; - float alpha = float(alpha_percent) * 0.01; - - //circular easing variable - float t = sin(elapsed_time * speed) * 2 - 1; - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } - float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); - float4 color = image.Sample(textureSampler, v_in.uv); - float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; - float4 gray = float4(luminance,luminance,luminance, 1); + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - b = BlurStyler(t, 0, c, d, ease); +} - const int no_colors = 3; - float4 s_color = start_color; - float4 m_color = middle_color; - float4 e_color = end_color; - if (rotate_colors) - { - // get general time number between 0 and 4 - float tx = (b + 1) * 2; - // 3 steps c1->c2, c2->c3, c3->c1 - //when between 0 - 1 only c1 rises then falls +} - if (tx <= 2.0) - { - s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); - } + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSOutput { - if ((tx >= 1.0) && (tx <= 3.0)) - { - s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); - } - if (tx >= 2.0) - { - s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); - } +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] +[Alias('obs.powershell.websocket.StopOutput')] +param( - if (tx < 0) - { - s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); - } - } +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) - float4 colors[no_colors]; - colors[0] =s_color; - colors[1] = m_color; - colors[2] = e_color; - float step[no_colors]; - step[0] = start_step; - step[1] = middle_step; - step[2] = end_step; - float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); - float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); - float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); +process { - float dist = distance(v_in.uv, gradient_center); - if (horizontal && (vertical == false)) - { - dist = distance(v_in.uv.y, gradient_center.y); - } - if (vertical && (horizontal == false)) - { - dist = distance(v_in.uv.x, gradient_center.x); - } - float4 col = colors[0]; - for (int i = 1; i < no_colors; ++i) { - col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); - } - col.a = clamp(alpha, 0.0, 1.0); - if (Apply_To_Alpha_Layer == false) - color.a = alpha; - if (Apply_To_Specific_Color) - { - col.a = alpha; - float4 original_color = image.Sample(textureSampler, v_in.uv); - col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; - } - // result = float4(redness, greenness,blueness,1); - //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); - //color.rgb += col * alpha; - //color.a += clamp(1.0 - alpha, 0.0,1.0); - ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); - //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); - color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); - - } - return color; - - -} - -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25772,191 +17659,101 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHalftoneShader { +function Stop-OBSRecord { -[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] +[Alias('obs.powershell.websocket.StopRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the threshold of OBSHalftoneShader -[ComponentModel.DefaultBindingProperty('threshold')] -[Single] -$Threshold, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'halftone' -$ShaderNoun = 'OBSHalftoneShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.1415926535897932384626433832795 -#define PI180 float(PI / 180.0) -uniform float threshold< - string label = "Threshold"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.6; -float sind(float a) -{ - return sin(a * PI180); -} - -float cosd(float a) -{ - return cos(a * PI180); -} - -float added(float2 sh, float sa, float ca, float2 c, float d) -{ - return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); -} - -float4 mainImage(VertData v_in) : TARGET -{ - // Halftone dot matrix shader - // @author Tomek Augustyn 2010 - - // Ported from my old PixelBender experiment - // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk - - float coordX = v_in.uv.x; - float coordY = v_in.uv.y; - float2 dstCoord = float2(coordX, coordY); - float2 rotationCenter = float2(0.5, 0.5); - float2 shift = dstCoord - rotationCenter; - - float dotSize = 3.0; - float angle = 45.0; - - float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); - float4 srcPixel = image.Sample(textureSampler, dstCoord); - - float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; - float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); - - // uncomment to see how the raster pattern looks - // gray = rasterPattern; - - return float4(gray, gray, gray, 1.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -25965,173 +17762,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHardBlinkShader { +function Stop-OBSReplayBuffer { -[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] +[Alias('obs.powershell.websocket.StopReplayBuffer')] param( -# Set the timeon of OBSHardBlinkShader -[ComponentModel.DefaultBindingProperty('timeon')] -[Single] -$Timeon, -# Set the timeoff of OBSHardBlinkShader -[ComponentModel.DefaultBindingProperty('timeoff')] -[Single] -$Timeoff, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hard_blink' -$ShaderNoun = 'OBSHardBlinkShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// hard_blink shader created by https://github.com/WhazzItToYa -// -// Periodically makes the source image 100% transparent, in configurable intervals. -uniform float timeon< - string label = "Time On"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; -uniform float timeoff< - string label = "Time Off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 0.5; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -float4 mainImage(VertData v_in) : TARGET -{ - float4 color = image.Sample(textureSampler, v_in.uv); - float m = timeon + timeoff; - float t = elapsed_time % m; - if (t < timeon) { - return color; - } else { - return float4(color.r, color.g, color.b, 0.0); - } -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26140,212 +17864,202 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHeatWaveSimpleShader { +function Stop-OBSStream { -[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] +[Alias('obs.powershell.websocket.StopStream')] param( -# Set the Rate of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Rate')] -[Single] -$Rate, -# Set the Strength of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Strength')] -[Single] -$Strength, -# Set the Distortion of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Distortion')] -[Single] -$Distortion, -# Set the Opacity of OBSHeatWaveSimpleShader -[ComponentModel.DefaultBindingProperty('Opacity')] -[Single] -$Opacity, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'heat-wave-simple' -$ShaderNoun = 'OBSHeatWaveSimpleShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Heat Wave Simple, Version 0.03, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// -// Description: -// Generate a crude pseudo heat wave displacement on an image source. -// -// Based on: https://www.shadertoy.com/view/td3GRn by Dombass -// -// Changelog: -// 0.03 - Added Opacity control -// 0.02 - Added crude Rate, Strength, and Distortion controls -// 0.01 - Initial release -uniform float Rate< - string label = "Rate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 5.0; -uniform float Strength< - string label = "Strength"; - string widget_type = "slider"; - float minimum = -25.0; - float maximum = 25.0; - float step = 0.01; -> = 1.0; -uniform float Distortion< - string label = "Distortion"; - string widget_type = "slider"; - float minimum = 3.0; - float maximum = 20.0; - float step = 0.01; -> = 10.0; -uniform float Opacity< - string label = "Opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.00; -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float distort = clamp(Distortion, 3.0, 20.0); + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - // Time varying pixel color - float jacked_time = Rate*elapsed_time; - float2 scale = float2(0.5, 0.5); - float str = clamp(Strength, -25.0, 25.0) * 0.01; - - uv += str * sin(scale*jacked_time + length( uv ) * distort); - float4 c = image.Sample( textureSampler, uv); - c.a *= saturate(Opacity*0.01); - return c; -} + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam } - continue nextParameter - } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +} - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Stop-OBSVirtualCam { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] +[Alias('obs.powershell.websocket.StopVirtualCam')] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26354,321 +18068,111 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHexagonShader { +function Switch-OBSInputMute { -[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] +[Alias('obs.powershell.websocket.ToggleInputMute')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the Hex_Color of OBSHexagonShader -[Alias('Hex_Color')] -[ComponentModel.DefaultBindingProperty('Hex_Color')] -[String] -$HexColor, -# Set the Alpha_Percent of OBSHexagonShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] -[Int32] -$AlphaPercent, -# Set the Quantity of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Quantity')] -[Single] -$Quantity, -# Set the Border_Width of OBSHexagonShader -[Alias('Border_Width')] -[ComponentModel.DefaultBindingProperty('Border_Width')] -[Int32] -$BorderWidth, -# Set the Blend of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Blend')] -[Management.Automation.SwitchParameter] -$Blend, -# Set the Equilateral of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Equilateral')] -[Management.Automation.SwitchParameter] -$Equilateral, -# Set the Zoom_Animate of OBSHexagonShader -[Alias('Zoom_Animate')] -[ComponentModel.DefaultBindingProperty('Zoom_Animate')] -[Management.Automation.SwitchParameter] -$ZoomAnimate, -# Set the Speed_Percent of OBSHexagonShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] -[Int32] -$SpeedPercent, -# Set the Glitch of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Distort_X of OBSHexagonShader -[Alias('Distort_X')] -[ComponentModel.DefaultBindingProperty('Distort_X')] -[Single] -$DistortX, -# Set the Distort_Y of OBSHexagonShader -[Alias('Distort_Y')] -[ComponentModel.DefaultBindingProperty('Distort_Y')] -[Single] -$DistortY, -# Set the Offset_X of OBSHexagonShader -[Alias('Offset_X')] -[ComponentModel.DefaultBindingProperty('Offset_X')] -[Single] -$OffsetX, -# Set the Offset_Y of OBSHexagonShader -[Alias('Offset_Y')] -[ComponentModel.DefaultBindingProperty('Offset_Y')] -[Single] -$OffsetY, -# Set the notes of OBSHexagonShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# The name of the source. This must be provided when adding an item for the first time + [Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +[ComponentModel.DefaultBindingProperty('inputName')] +[string] +$InputName, + [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[ComponentModel.DefaultBindingProperty('inputUuid')] +[string] +$InputUuid, +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hexagon' -$ShaderNoun = 'OBSHexagonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 -//https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 Hex_Color; -uniform int Alpha_Percent< - string label = "Alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform float Quantity< - string label = "Quantity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 25; -uniform int Border_Width< - string label = "Border Width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 115; - int step = 1; -> = 15; // <- -15 to 85, -15 off top -uniform bool Blend; -uniform bool Equilateral; -uniform bool Zoom_Animate; -uniform int Speed_Percent< - string label = "Speed Percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 100; -uniform bool Glitch; -uniform float Distort_X< - string label = "Distort X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Distort_Y< - string label = "Distort Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float Offset_X< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Offset_Y< - string label = "Offset X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform string notes< - string widget_type = "info"; ->= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; - -float mod(float x, float y) -{ - return x - y * floor(x/y); -} - -float2 mod2(float2 x, float2 y) -{ - return x - y * floor(x/y); -} - -// 0 on edges, 1 in non_edge -float hex(float2 p) { - float xyratio = 1; - if (Equilateral) - xyratio = uv_size.x /uv_size.y; - - // calc p - p.x = mul(p.x,xyratio); - p.y += mod(floor(p.x) , 2.0)*0.5; - p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); - return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); - float alpha = float(Alpha_Percent) * 0.01; - float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); - float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; - float speed = float(Speed_Percent) * 0.01; - float time = (1 + sin(elapsed_time * speed))*0.5; - if (Zoom_Animate) - quantity *= time; - - // create a (pos)ition reference, hex radius and smoothstep out the non_edge - float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); - if (Glitch) - quantity *= lerp(pos.x, pos.y, rand_f); - float2 p = (pos * quantity); // number of hexes to be created - float r = (1.0 -0.7)*0.5; // cell default radius - float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge - // make the border colorable - non_edge is scaled - float4 c = float4(non_edge, non_edge,non_edge,1.0) ; - if (non_edge < 1) - { - c = Hex_Color; - c.a = alpha; - if (Blend) - c = lerp(rgba, c, 1 - non_edge); - return lerp(rgba,c,alpha); - } - return lerp(rgba, c * rgba, alpha); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26677,316 +18181,209 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHslHsvSaturationShader { +function Switch-OBSOutput { -[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] +[Alias('obs.powershell.websocket.ToggleOutput')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the hslSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] -[Single] -$HslSaturationFactor, -# Set the hslGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hslGamma')] -[Single] -$HslGamma, -# Set the hsvSaturationFactor of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] -[Single] -$HsvSaturationFactor, -# Set the hsvGamma of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('hsvGamma')] -[Single] -$HsvGamma, -# Set the adjustmentOrder of OBSHslHsvSaturationShader -[ComponentModel.DefaultBindingProperty('adjustmentOrder')] -[Int32] -$AdjustmentOrder, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. + +[Parameter(Mandatory,ValueFromPipelineByPropertyName)] +[ComponentModel.DefaultBindingProperty('outputName')] +[string] +$OutputName, +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hsl_hsv_saturation' -$ShaderNoun = 'OBSHslHsvSaturationShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' - -// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions - -uniform float hslSaturationFactor< - string label = "HSL Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; - -uniform float hslGamma< - string label = "HSL Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float hsvSaturationFactor< - string label = "HSV Sat Gain"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.01; -> = 1.0; -uniform float hsvGamma< - string label = "HSV Sat Gamma"; - string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 1.0; + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand -uniform int adjustmentOrder< - string label = "Order"; - string widget_type = "select"; - int option_0_value = 1; - string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; - int option_1_value = 2; - string option_1_label = "HSL first, then HSV"; - int option_2_value = 3; - string option_2_label = "HSV first, then HSL"; -> = 1; + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } -// HSV conversion + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } + } -float3 rgb2hsv(float3 c) { - float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); - float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } -float3 hsv2rgb(float3 c) { - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } -// HSL conversion +} -float3 rgb2hsl(float3 c) { - float maxVal = max(c.r, max(c.g, c.b)); - float minVal = min(c.r, min(c.g, c.b)); - float delta = maxVal - minVal; - float h = 0.0; - float s = 0.0; - float l = (maxVal + minVal) / 2.0; - if(delta != 0) { - if(l < 0.5) s = delta / (maxVal + minVal); - else s = delta / (2.0 - maxVal - minVal); +} - if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); - else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; - else h = (c.r - c.g) / delta + 4.0; + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSRecord { - h /= 6.0; - } - return float3(h, s, l); -} - -float hue2rgb(float p, float q, float t) { - if(t < 0.0) t += 1.0; - if(t > 1.0) t -= 1.0; - if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; - if(t < 1.0/2.0) return q; - if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; - return p; -} - -float3 hsl2rgb(float3 c) { - float r, g, b; - - if(c.y == 0.0) { - r = g = b = c.z; - } else { - float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; - float p = 2.0 * c.z - q; - r = hue2rgb(p, q, c.x + 1.0/3.0); - g = hue2rgb(p, q, c.x); - b = hue2rgb(p, q, c.x - 1.0/3.0); - } - - return float3(r, g, b); -} +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] +[Alias('obs.powershell.websocket.ToggleRecord')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) -float3 adjustColorWithOrder(float3 originalColor) { - if (adjustmentOrder == 1) { - // Parallel adjustment (both HSL and HSV operate on the original image and then blend) - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 hslAdjustedColor = hsl2rgb(hslAdjusted); - - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); - - float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; - return finalColor; - } - else if (adjustmentOrder == 2) { - // HSL first, then HSV - float3 hslAdjusted = rgb2hsl(originalColor); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - float3 afterHSL = hsl2rgb(hslAdjusted); - float3 hsvAdjusted = rgb2hsv(afterHSL); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - return hsv2rgb(hsvAdjusted); - } - else if (adjustmentOrder == 3) { - // HSV first, then HSL - float3 hsvAdjusted = rgb2hsv(originalColor); - hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); - hsvAdjusted.y *= hsvSaturationFactor; - float3 afterHSV = hsv2rgb(hsvAdjusted); - float3 hslAdjusted = rgb2hsl(afterHSV); - hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); - hslAdjusted.y *= hslSaturationFactor; - return hsl2rgb(hslAdjusted); - } - return originalColor; // Default to original color in case of unexpected values -} -// Final composite +process { -float4 mainImage(VertData v_in) : TARGET -{ - float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; - float3 adjustedColor = adjustColorWithOrder(originalColor); - return float4(adjustedColor, 1.0); // preserving the original alpha -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -26995,235 +18392,100 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSHueRotatonShader { +function Switch-OBSRecordPause { -[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] +[Alias('obs.powershell.websocket.ToggleRecordPause')] param( -# Set the Speed of OBSHueRotatonShader -[ComponentModel.DefaultBindingProperty('Speed')] -[Single] -$Speed, -# Set the Hue_Override of OBSHueRotatonShader -[Alias('Hue_Override')] -[ComponentModel.DefaultBindingProperty('Hue_Override')] -[Management.Automation.SwitchParameter] -$HueOverride, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'hue-rotaton' -$ShaderNoun = 'OBSHueRotatonShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Hue Rotation shader, version 1.0 for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: GNU General Public License, version 2 -// -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Description: -// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue -// override option is provided to force a specific rotating hue instead of the original image''s hue. -// -// Changelog: -// 1.0 - Initial release - -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -uniform float Speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 10.00; -uniform bool Hue_Override = false; - -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return saturate(float3(R,G,B)); -} - -#define Epsilon 1e-10 - -float3 RGBtoHCV(in float3 RGB) -{ - // Based on work by Sam Hocevar and Emil Persson - float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); - float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); - float C = Q.x - min(Q.w, Q.y); - float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); - return float3(H, C, Q.x); -} - -float3 HSVtoRGB(in float3 HSV) -{ - float3 RGB = HUEtoRGB(HSV.x); - return ((RGB - 1) * HSV.y + 1) * HSV.z; -} - -float3 RGBtoHSV(in float3 RGB) -{ - float3 HCV = RGBtoHCV(RGB); - float S = HCV.y / (HCV.z + Epsilon); - return float3(HCV.x, S, HCV.z); -} - -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 col_in = image.Sample(textureSampler, uv); - float3 col_out; - float3 HSV = RGBtoHSV(col_in.rgb); - - if(Hue_Override) - HSV.x = elapsed_time * Speed * 0.01; - else - HSV.x += elapsed_time * Speed * 0.01; - // Normalize Hue - HSV.x = frac(HSV.x); - - col_out = HSVtoRGB(HSV); - return float4(col_out, col_in.a); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } } - - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -27232,192 +18494,307 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSIntensityScopeShader { +function Switch-OBSReplayBuffer { -[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] +[Alias('obs.powershell.websocket.ToggleReplayBuffer')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] param( -# Set the gain of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('gain')] -[Single] -$Gain, -# Set the blend of OBSIntensityScopeShader -[ComponentModel.DefaultBindingProperty('blend')] -[Single] -$Blend, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. +# If set, will return the information that would otherwise be sent to OBS. [Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] +[Alias('OutputRequest','OutputInput')] +[switch] $PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse ) process { -$shaderName = 'intensity-scope' -$ShaderNoun = 'OBSIntensityScopeShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Robin Green, Dec 2016 -// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. -// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro -uniform float gain< - string label = "Gain"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 1.00; - float step = 0.01; -> = 0.3; -uniform float blend< - string label = "Blend"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.00; - float step = 0.01; -> = 0.6; -float4 mainImage(VertData v_in) : TARGET -{ - float2 uv = v_in.uv; - uv.y = 1.0 - uv.y; - - // calculate the intensity bucket for this pixel based on column height (padded at the top) - const float max_value = 270.0; - const float buckets = 512.0; - float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); - float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); - - // count the count the r,g,b and luma in this column that match the bucket - float4 count = float4(0.0, 0.0, 0.0, 0.0); - for( int i=0; i < 512; ++i ) { - float j = float(i) / buckets; - float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; - - // calculate the Rec.709 luma for this pixel - pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; - float4 logpixel = log(pixel); - if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; - if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; - if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; - if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; - } - - // sum luma into RGB, tweak log intensity for readability - count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; - - // output - return count; -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy + } + + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSStream { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] +[Alias('obs.powershell.websocket.ToggleStream')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} + } + + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, } - continue nextParameter - } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } + if ($PassThru) { + [PSCustomObject]$requestPayload + } else { + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse + } - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Switch-OBSVirtualCam { + + +[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] +[Alias('obs.powershell.websocket.ToggleVirtualCam')] +[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +param( +# If set, will return the information that would otherwise be sent to OBS. +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('OutputRequest','OutputInput')] +[switch] +$PassThru, +# If set, will not attempt to receive a response from OBS. +# This can increase performance, and also silently ignore critical errors +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] +[switch] +$NoResponse +) + + +process { + + + # Create a copy of the parameters (that are part of the payload) + $paramCopy = [Ordered]@{} + # get a reference to this command + $myCmd = $MyInvocation.MyCommand + + # Keep track of how many requests we have done of a given type + # (this makes creating RequestIDs easy) + if (-not $script:ObsRequestsCounts) { + $script:ObsRequestsCounts = @{} } - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} + # Set my requestType to blank + $myRequestType = '' + # and indicate we are not expecting a response + $responseExpected = $false + # Then walk over this commands' attributes, + foreach ($attr in $myCmd.ScriptBlock.Attributes) { + if ($attr -is [Reflection.AssemblyMetadataAttribute]) { + if ($attr.Key -eq 'OBS.WebSocket.RequestType') { + $myRequestType = $attr.Value # set the requestType, + } + elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { + # and determine if we are expecting a response. + $responseExpected = + if ($attr.Value -eq 'false') { + $false + } else { $true } + } + } } - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + # Walk over each parameter + :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { + # and walk over each of it's attributes to see if it part of the payload + foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { + # If the parameter is bound to part of the payload + if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { + # copy it into our payload dicitionary. + $paramCopy[$attr.Name] = $keyValue.Value + # (don't forget to turn switches into booleans) + if ($paramCopy[$attr.Name] -is [switch]) { + $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + if ($attr.Name -like '*path') { + $paramCopy[$attr.Name] = + "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + continue nextParam + } + } + } + + # and make a request ID from that. + $myRequestId = "$myRequestType.$([Guid]::newGuid())" + + # Construct the payload object + $requestPayload = [Ordered]@{ + # It must include a request ID + requestId = $myRequestId + # request type + requestType = $myRequestType + # and optional data + requestData = $paramCopy } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat + if ($PassThru) { + [PSCustomObject]$requestPayload } else { - Set-OBSShaderFilter @ShaderFilterSplat + [PSCustomObject]$requestPayload | + Send-OBS -NoResponse:$NoResponse } - } -} } @@ -27426,30 +18803,70 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInvertLumaShader { +function Get-OBS3dPanelShader { -[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] +[Alias('Set-OBS3dPanelShader','Add-OBS3dPanelShader')] param( -# Set the Invert_Color of OBSInvertLumaShader -[Alias('Invert_Color')] -[ComponentModel.DefaultBindingProperty('Invert_Color')] -[Management.Automation.SwitchParameter] -$InvertColor, -# Set the Invert_Luma of OBSInvertLumaShader -[Alias('Invert_Luma')] -[ComponentModel.DefaultBindingProperty('Invert_Luma')] -[Management.Automation.SwitchParameter] -$InvertLuma, -# Set the Gamma_Correction of OBSInvertLumaShader -[Alias('Gamma_Correction')] -[ComponentModel.DefaultBindingProperty('Gamma_Correction')] -[Management.Automation.SwitchParameter] -$GammaCorrection, -# Set the Test_Ramp of OBSInvertLumaShader -[Alias('Test_Ramp')] -[ComponentModel.DefaultBindingProperty('Test_Ramp')] +# Set the credits of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('credits')] +[String] +$Credits, +# Set the scale of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the tilt_x_deg of OBS3dPanelShader +[Alias('tilt_x_deg')] +[ComponentModel.DefaultBindingProperty('tilt_x_deg')] +[Single] +$TiltXDeg, +# Set the tilt_y_deg of OBS3dPanelShader +[Alias('tilt_y_deg')] +[ComponentModel.DefaultBindingProperty('tilt_y_deg')] +[Single] +$TiltYDeg, +# Set the tilt_z_deg of OBS3dPanelShader +[Alias('tilt_z_deg')] +[ComponentModel.DefaultBindingProperty('tilt_z_deg')] +[Single] +$TiltZDeg, +# Set the pos_x of OBS3dPanelShader +[Alias('pos_x')] +[ComponentModel.DefaultBindingProperty('pos_x')] +[Single] +$PosX, +# Set the pos_y of OBS3dPanelShader +[Alias('pos_y')] +[ComponentModel.DefaultBindingProperty('pos_y')] +[Single] +$PosY, +# Set the thickness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('thickness')] +[Single] +$Thickness, +# Set the radius_fb of OBS3dPanelShader +[Alias('radius_fb')] +[ComponentModel.DefaultBindingProperty('radius_fb')] +[Single] +$RadiusFb, +# Set the brightness of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the light_position of OBS3dPanelShader +[Alias('light_position')] +[ComponentModel.DefaultBindingProperty('light_position')] +[Int32] +$LightPosition, +# Set the wiggle of OBS3dPanelShader +[ComponentModel.DefaultBindingProperty('wiggle')] +[Single] +$Wiggle, +# Set the wiggle_rot of OBS3dPanelShader +[Alias('wiggle_rot')] +[ComponentModel.DefaultBindingProperty('wiggle_rot')] [Management.Automation.SwitchParameter] -$TestRamp, +$WiggleRot, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27480,121 +18897,265 @@ $UseShaderTime process { -$shaderName = 'invert-luma' -$ShaderNoun = 'OBSInvertLumaShader' +$shaderName = '3d-panel' +$ShaderNoun = 'OBS3dPanelShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Invert shader 1.0 - for OBS Shaderfilter -// Copyright 2021 by SkeletonBow -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv +//based on https://x.com/HoraiChan/status/1986268258883010766 -// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling +uniform string credits< + string widget_type = "info"; +> = "Based on effect by Horaiken"; -uniform bool Invert_Color = false; -uniform bool Invert_Luma = true; -uniform bool Gamma_Correction = true; -uniform bool Test_Ramp = false; +uniform float scale< + string label = "大きさ / Scale"; + string widget_type = "slider"; + float minimum = 0.25; + float maximum = 3.00; + float step = 0.001; +> = 1.0; +uniform float tilt_x_deg< + string label = "縦方向の傾き(X) / Tilt (X)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 20.0; +uniform float tilt_y_deg< + string label = "横方向の傾き(Y) / Tilt (Y)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 35.0; +uniform float tilt_z_deg< + string label = "回転(Z) / Roll (Z)"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.00; + float step = 0.1; +> = 0.0; +uniform float pos_x< + string label = "横位置 / Horizontal Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float pos_y< + string label = "縦位置 / Vertical Position"; + string widget_type = "slider"; + float minimum = -1.00; + float maximum = 1.00; + float step = 0.0001; +> = 0.0; +uniform float thickness< + string label = "厚み / Thickness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 0.1; + float step = 0.001; +> = 0.03; +uniform float radius_fb< + string label = "角丸 / Corner Radius"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.2; +uniform float brightness< + string label = "明るさ / Brightness"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.00; + float step = 0.01; +> = 1.2; +uniform int light_position < + string label = "照明の位置 / Light Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "左側に光 / Light From Left"; + int option_1_value = 1; + string option_1_label = "右側に光 / Light From Right"; +> = 0; +uniform float wiggle < + string label = "ゆらゆら / Wiggle"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 2.50; + float step = 0.01; +> = 0.0; +uniform bool wiggle_rot < + string label = "角度もゆらゆら / Wiggle Rotation"; +>; -float3 encodeSRGB(float3 linearRGB) -{ - float3 a = float3(12.92,12.92,12.92) * linearRGB; - float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); - float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); - return float3(lerp(a, b, c)); +float hash1(float n){ return frac(sin(n)*43758.5453123); } + +float noise1D(float x) { + float i = floor(x); + float f = frac(x); + float u = f*f*(3.0 - 2.0*f); + return lerp(hash1(i), hash1(i+1.0), u); // 0..1 } -float3 decodeSRGB(float3 screenRGB) -{ - float3 a = screenRGB / float3(12.92,12.92,12.92); - float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); - float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); - return float3(lerp(a, b, c)); +float fbm1D(float x) { + float v = 0.0; + float a = 0.5; + float f = 1.0; + for(int k=0;k<4;k++){ + v += a * noise1D(x * f); + f *= 2.0; + a *= 0.5; + } + return v; } -float3 HUEtoRGB(in float H) -{ - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); +float saturate(float x) { return clamp(x, 0.0, 1.0); } + +float3 rotateX(float3 p, float a){ float c=cos(a), s=sin(a); return float3(p.x, c*p.y - s*p.z, s*p.y + c*p.z); } +float3 rotateY(float3 p, float a){ float c=cos(a), s=sin(a); return float3( c*p.x + s*p.z, p.y, -s*p.x + c*p.z); } +float3 rotateZ(float3 p, float a){ float c=cos(a), s=sin(a); return float3(c*p.x - s*p.y, s*p.x + c*p.y, p.z); } + +// 2D 角丸長方形 SDF(中心、半径 bxy, 角丸 r) +float sdRoundRect2D(float2 p, float2 bxy, float r) { + float2 q = abs(p) - bxy + r; + return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; } -float3 RGBtoYUV(float3 color) -{ - // YUV matriz (BT709 luma coefficients) -#ifdef OPENGL - float3x3 toYUV = float3x3( - float3(0.2126, -0.09991, 0.615), - float3(0.7152, -0.33609, -0.55861), - float3(0.0722, 0.436, -0.05639)); -#else - float3x3 toYUV = { - { 0.2126, -0.09991, 0.615 }, - { 0.7152, -0.33609, -0.55861 }, - { 0.0722, 0.436, -0.05639 }, - }; -#endif - return mul(color, toYUV); +// 正面シルエット角丸 + Z方向に押し出し +float sdFrontViewRoundedPrism(float3 p, float3 b, float r_fb_norm) { + float r_fb = saturate(r_fb_norm) * (0.999 * min(b.x, b.y)); + float a = sdRoundRect2D(p.xy, b.xy, r_fb); + float dz = abs(p.z) - b.z; + return max(a, dz); } -float3 YUVtoRGB(float3 color) -{ - // YUV matriz (BT709) -#ifdef OPENGL - float3x3 fromYUV = float3x3( - float3(1.000, 1.000, 1.000), - float3(0.0, -0.21482, 2.12798), - float3(1.28033, -0.38059, 0.0)); -#else - float3x3 fromYUV = { - { 1.000, 1.000, 1.000 }, - { 0.0, -0.21482, 2.12798 }, - { 1.28033, -0.38059, 0.0 }, - }; -#endif - return mul(color, fromYUV); +// 法線 +float3 calcNormal(float3 p, float3 b, float rfb) { + const float e = 0.001; + float3 ex=float3(e,0,0), ey=float3(0,e,0), ez=float3(0,0,e); + float dx = sdFrontViewRoundedPrism(p+ex,b,rfb) - sdFrontViewRoundedPrism(p-ex,b,rfb); + float dy = sdFrontViewRoundedPrism(p+ey,b,rfb) - sdFrontViewRoundedPrism(p-ey,b,rfb); + float dz = sdFrontViewRoundedPrism(p+ez,b,rfb) - sdFrontViewRoundedPrism(p-ez,b,rfb); + return normalize(float3(dx,dy,dz)); } -float3 generate_ramps(float3 color, float2 uv) -{ - float3 ramp = float3(0.0, 0.0, 0.0); - if(uv.y < 0.2) - ramp.r = uv.x; // Red ramp - else if(uv.y < 0.4) - ramp.g = uv.x; // Green ramp - else if(uv.y < 0.6) - ramp.b = uv.x; // Blue ramp - else if(uv.y < 0.8) - ramp = float3(uv.x, uv.x, uv.x); // Grey ramp - else - ramp = HUEtoRGB(uv.x); // Hue rainbow - - return ramp; +// 照明 +float3 shade(float3 n, float3 v) { + float3 l; + if (light_position == 0) { // 左から光 + l = normalize(float3(-1.0, -0.1, 1.0)); + } + else { // 右から光 + l = normalize(float3( 1.0, -0.1, 1.0)); + } + float diff = saturate(dot(n,l)); + float rim = pow(1.0 - saturate(dot(n,v)), 2.0); + float li = 0.25 + 0.75*diff + 0.08*rim; + return float3(li, li, li); } -float4 mainImage( VertData v_in ) : TARGET -{ - float2 uv = v_in.uv; - float4 obstex = image.Sample( textureSampler, uv ); - float3 color = obstex.rgb; - // Apply sRGB gamma transfer encode - if(Gamma_Correction) color = encodeSRGB( color ); - // Override display with test patterns to visually see what is happening - if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); - // RGB color invert - if( Invert_Color ) { - color = float3(1.0, 1.0, 1.0) - color; - } - // YUV luma invert - if( Invert_Luma ) { - float3 yuv = RGBtoYUV( color ); - yuv.x = 1.0 - yuv.x; - color = YUVtoRGB(yuv); - } - // Apply sRGB gamma transfer decode - if(Gamma_Correction) color = decodeSRGB( color ); - return float4(color, obstex.a); +float4 mainImage(VertData v_in) : TARGET { + float2 uv = v_in.uv; + + // 画面座標(短辺基準) + float aspect = uv_size.x / uv_size.y; + float2 ndc = uv * 2.0 - 1.0; + ndc += float2(pos_x, pos_y) * -1.0 * (scale + 1.0); + float2 p2 = ndc; + p2.x *= aspect; + + // カメラ設定 + float3 ro = float3(0.0, 0.0, 3.2); + float3 rd = normalize(float3(p2, -4.0)); + + // 回転(Z→Y→X の順に逆回転) + float ax=radians(tilt_x_deg), ay=radians(tilt_y_deg), az=radians(tilt_z_deg); + ro = rotateX(rotateY(rotateZ(ro, -az), -ay), -ax); + rd = normalize(rotateX(rotateY(rotateZ(rd, -az), -ay), -ax)); + + // 画面フィット(短辺基準)+ 厚み + float2 baseXY; + if (aspect > 1.0) { + baseXY = float2(1.0, 1.0 / aspect); + } else { + const float portraitMargin = 0.6; + baseXY = float2(aspect * portraitMargin, 1.0 * portraitMargin); + } + float3 b = float3(baseXY, thickness) * max(scale, 0.0001); + + // Wiggle + float diag = length(2.0 * b); + float amp = 0.05 * wiggle * diag; + const float WSPD = 0.1; + + // 各軸に独立ノイズ + float wx = (fbm1D(elapsed_time*WSPD + 13.37) * 2.0 - 1.0) * amp; + float wy = (fbm1D(elapsed_time*WSPD + 47.11) * 2.0 - 1.0) * amp; + float wz = (fbm1D(elapsed_time*WSPD + 91.73) * 2.0 - 1.0) * amp * 0.35; + float3 woff = float3(wx, wy, wz); + + float rotAmp = radians(12.0) * wiggle; + + float wobX = (fbm1D(elapsed_time*WSPD + 128.31) * 2.0 - 1.0) * rotAmp; + float wobY = (fbm1D(elapsed_time*WSPD + 299.91) * 2.0 - 1.0) * rotAmp; + + float3 ro2 = ro; + float3 rd2 = rd; + + if (wiggle_rot) { + ro2 = rotateX(ro2, wobX); + ro2 = rotateY(ro2, wobY); + rd2 = rotateX(rd2, wobX); + rd2 = rotateY(rd2, wobY); + } + + float t = 0.0; + float d = 0.0; + bool hit = false; + for (int i=0; i<64; i++) { + float3 pos = ro2 + rd2 * t; + d = sdFrontViewRoundedPrism(pos - woff, b, radius_fb); + if (d < 0.001) { hit = true; break; } + t += d; + if (t > 8.0) break; + } + + // ヒットしなければ完全透明(元ソースは非表示) + if (!hit) return float4(0.0, 0.0, 0.0, 0.0); + + float3 pos = ro2 + rd2 * t; + float3 pObj = pos - woff; + float3 n = calcNormal(pObj, b, radius_fb); + float3 vdir = normalize(-rd2); + + // テクスチャ貼り付け + float frontMask = smoothstep(0.5, 0.8, dot(n, float3(0.0, 0.0, 1.0))); + float2 uvTex = (pObj.xy / b.xy) * 0.5 + 0.5; + + // サンプル(Address Clamp なので側面/背面は端ピクセルが“引き伸ばし”) + float4 texFront = image.Sample(textureSampler, uvTex); + float4 texEdge = image.Sample(textureSampler, uvTex); + float4 tex = lerp(texEdge, texFront, frontMask); + + // フロント面エッジ・ハイライト(細い線) + float r_fb = saturate(radius_fb) * (0.999 * min(b.x, b.y)); + float a_xy = sdRoundRect2D(pObj.xy, b.xy, r_fb); // XY角丸SDF + float edgeWidth = 0.02 * min(b.x, b.y); + float edgeIntensity = 0.6; + float edgeProx = 1.0 - saturate(abs(a_xy) / edgeWidth); + float edgeMask = frontMask * edgeProx; + tex.rgb *= (1.0 + edgeMask * edgeIntensity); + + // 照明 + float3 lightTerm = shade(n, vdir); + tex.rgb *= lightTerm; + + // 明るさスライダ + tex.rgb *= brightness; + + // 出力 + return float4(tex.rgb, 1.0); } ' @@ -27694,42 +19255,47 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminance2Shader { +function Get-OBS3dSwapTransitionShader { -[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] +[Alias('Set-OBS3dSwapTransitionShader','Add-OBS3dSwapTransitionShader')] param( -# Set the color of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('color')] +# Set the image_a of OBS3dSwapTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] [String] -$Color, -# Set the lumaMax of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMax')] +$ImageA, +# Set the image_b of OBS3dSwapTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBS3dSwapTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] [Single] -$LumaMax, -# Set the lumaMin of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMin')] +$TransitionTime, +# Set the convert_linear of OBS3dSwapTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the reflection of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('reflection')] [Single] -$LumaMin, -# Set the lumaMaxSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] +$Reflection, +# Set the perspective of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('perspective')] [Single] -$LumaMaxSmooth, -# Set the lumaMinSmooth of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +$Perspective, +# Set the depth of OBS3dSwapTransitionShader +[ComponentModel.DefaultBindingProperty('depth')] [Single] -$LumaMinSmooth, -# Set the invertImageColor of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminance2Shader -[ComponentModel.DefaultBindingProperty('notes')] +$Depth, +# Set the background_color of OBS3dSwapTransitionShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] [String] -$Notes, +$BackgroundColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -27760,91 +19326,115 @@ $UseShaderTime process { -$shaderName = 'luminance2' -$ShaderNoun = 'OBSLuminance2Shader' +$shaderName = '3d_swap_transition' +$ShaderNoun = 'OBS3dSwapTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform float4 color; -uniform float lumaMax< - string label = "Luma Max"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.05; -uniform float lumaMin< - string label = "Luma Min"; +//based on https://www.shadertoy.com/view/MlXGzf + +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; + +uniform float reflection< + string label = "Reflection (0.4)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMaxSmooth< - string label = "Luma Max Smooth"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.4; +uniform float perspective< + string label = "Perspective (0.2)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.10; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = .2; +uniform float depth< + string label = "Depth (3.0)"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; -uniform string notes< - string widget_type = "info"; -> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; - -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} - -float4 mainImage(VertData v_in) : TARGET -{ - - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + float minimum = 1.00; + float maximum = 10.00; + float step = 0.1; +> = 3.; - //intensity = min(max(intensity,minIntensity),maxIntensity); - float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); - float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); +#ifndef OPENGL +#define lessThan(a,b) (a < b) +#endif - float amask = clo * chi; - if (invertAlphaChannel) - { - amask = 1.0 - amask; - } - rgba *= color; - rgba.a = clamp(amask, 0.0, 1.0); - - } - return rgba; +uniform float4 background_color = {0.0, 0.0, 0.0, 1.0}; + +bool inBounds (float2 p) { + return all(lessThan(float2(0.0,0.0), p)) && all(lessThan(p, float2(1.0,1.0))); } - -' + +float2 project (float2 p) { + return p * float2(1.0, -1.2) + float2(0.0, 2.22); } -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' + +float4 bgColor (float2 p, float2 pfr, float2 pto) { + float4 c = background_color; + pfr = project(pfr); + if (inBounds(pfr)) { + c += lerp(background_color, image_a.Sample(textureSampler, pfr), reflection * lerp(0.0, 1.0, pfr.y)); + } + pto = project(pto); + if (inBounds(pto)) { + c += lerp(background_color, image_b.Sample(textureSampler, pto), reflection * lerp(0.0, 1.0, pto.y)); + } + return c; +} + +float4 mainImage(VertData v_in) : TARGET { + float2 p = v_in.uv; + float2 pfr = float2(-1.,-1.); + float2 pto = float2(-1.,-1.); + + float progress = transition_time; + float size = lerp(1.0, depth, progress); + float persp = perspective * progress; + pfr = (p + float2(-0.0, -0.5)) * float2(size/(1.0-perspective*progress), size/(1.0-size*persp*p.x)) + float2(0.0, 0.5); + + size = lerp(1.0, depth, 1.-progress); + persp = perspective * (1.-progress); + pto = (p + float2(-1.0, -0.5)) * float2(size/(1.0-perspective*(1.0-progress)), size/(1.0-size*persp*(0.5-p.x))) + float2(1.0, 0.5); + + bool fromOver = progress < 0.5; + float4 rgba = background_color; + if (fromOver) { + if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + else { + if (inBounds(pto)) { + rgba = image_b.Sample(textureSampler, pto); + } + else if (inBounds(pfr)) { + rgba = image_a.Sample(textureSampler, pfr); + } + else { + rgba = bgColor(p, pfr, pto); + } + } + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' } switch -regex ($myVerb) { Get { @@ -27936,75 +19526,15 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceAlphaShader { +function Get-OBSAddShader { -[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] +[Alias('Set-OBSAddShader','Add-OBSAddShader')] param( -# Set the ViewProj of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSLuminanceAlphaShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSLuminanceAlphaShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSLuminanceAlphaShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSLuminanceAlphaShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSLuminanceAlphaShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSLuminanceAlphaShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the color_matrix of OBSLuminanceAlphaShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('color')] +# Set the other_image of OBSAddShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] [String] -$Color, -# Set the mul_val of OBSLuminanceAlphaShader -[Alias('mul_val')] -[ComponentModel.DefaultBindingProperty('mul_val')] -[Single] -$MulVal, -# Set the add_val of OBSLuminanceAlphaShader -[Alias('add_val')] -[ComponentModel.DefaultBindingProperty('add_val')] -[Single] -$AddVal, -# Set the level of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('level')] -[Single] -$Level, -# Set the invertAlphaChannel of OBSLuminanceAlphaShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, +$OtherImage, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28035,110 +19565,19 @@ $UseShaderTime process { -$shaderName = 'luminance_alpha' -$ShaderNoun = 'OBSLuminanceAlphaShader' +$shaderName = 'Add' +$ShaderNoun = 'OBSAddShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; - -uniform float4x4 color_matrix; -uniform float4 color; -uniform float mul_val< - string label = "Mulitply"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float add_val< - string label = "Add"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 0.0; -uniform float level< - string label = "Level"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> =1.0; -uniform bool invertAlphaChannel; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertDataIn { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; -}; - -VertDataOut mainTransform(VertDataIn v_in) -{ - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); - vert_out.uv = v_in.uv * mul_val + add_val; - vert_out.uv2 = v_in.uv ; - return vert_out; -} - -/*float3 GetLuminance(float4 rgba) -{ - float red = rbga.r; - float green = rgba.g; - float blue = rgba.b; - return (.299 * red) + (.587 * green) + (.114 * blue); -}*/ - -float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv) ; - if (rgba.a > 0.0) - { - - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; - } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - - } - return rgba; -} +uniform texture2d other_image; -technique Draw +float4 mainImage(VertData v_in) : TARGET { - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = PSAlphaMaskRGBA(v_in); - } + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return clamp(base + other, 0.0, 1.0); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -28236,30 +19675,25 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLuminanceShader { +function Get-OBSAlphaBorderShader { -[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] +[Alias('Set-OBSAlphaBorderShader','Add-OBSAlphaBorderShader')] param( -# Set the color of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('color')] +# Set the border_color of OBSAlphaBorderShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] [String] -$Color, -# Set the level of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('level')] +$BorderColor, +# Set the border_thickness of OBSAlphaBorderShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the alpha_cut_off of OBSAlphaBorderShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] [Single] -$Level, -# Set the invertImageColor of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertImageColor')] -[Management.Automation.SwitchParameter] -$InvertImageColor, -# Set the invertAlphaChannel of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] -[Management.Automation.SwitchParameter] -$InvertAlphaChannel, -# Set the notes of OBSLuminanceShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$AlphaCutOff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28290,62 +19724,44 @@ $UseShaderTime process { -$shaderName = 'Luminance' -$ShaderNoun = 'OBSLuminanceShader' +$shaderName = 'alpha_border' +$ShaderNoun = 'OBSAlphaBorderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 22, 2022 -uniform float4 color; -uniform float level< - string label = "Level"; +uniform float4 border_color< + string label = "Border color"; +> = {0.0,0.0,0.0,1.0}; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; + float maximum = 1.0; float step = 0.01; -> = 1.0; -uniform bool invertImageColor; -uniform bool invertAlphaChannel; - -uniform string notes< - string widget_type = "info"; -> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; - -float4 InvertColor(float4 rgba_in) -{ - rgba_in.r = 1.0 - rgba_in.r; - rgba_in.g = 1.0 - rgba_in.g; - rgba_in.b = 1.0 - rgba_in.b; - rgba_in.a = 1.0 - rgba_in.a; - return rgba_in; -} +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - - float4 rgba = image.Sample(textureSampler, v_in.uv); - if (rgba.a > 0.0) - { - - if (invertImageColor) - { - rgba = InvertColor(rgba); - } - float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - - //intensity = min(max(intensity,minIntensity),maxIntensity); - - - if (invertAlphaChannel) - { - intensity = 1.0 - intensity; - } - rgba *= color; - rgba.a = clamp((intensity * level), 0.0, 1.0); - + float4 pix = image.Sample(textureSampler, v_in.uv); + if (pix.a > alpha_cut_off) + return pix; + [loop] for(int x = -border_thickness;x alpha_cut_off) + return border_color; + } + } } - return rgba; + return pix; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -28443,98 +19859,60 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMatrixShader { +function Get-OBSAlphaGamingBentCameraShader { -[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] +[Alias('Set-OBSAlphaGamingBentCameraShader','Add-OBSAlphaGamingBentCameraShader')] param( -# Set the ViewProj of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSMatrixShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the left_side_width of OBSAlphaGamingBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSMatrixShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSMatrixShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_size of OBSMatrixShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the uv_pixel_interval of OBSMatrixShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSMatrixShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$LeftSideWidth, +# Set the left_side_size of OBSAlphaGamingBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] [Single] -$RandF, -# Set the rand_instance_f of OBSMatrixShader -[Alias('rand_instance_f')] -[ComponentModel.DefaultBindingProperty('rand_instance_f')] +$LeftSideSize, +# Set the left_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] [Single] -$RandInstanceF, -# Set the rand_activation_f of OBSMatrixShader -[Alias('rand_activation_f')] -[ComponentModel.DefaultBindingProperty('rand_activation_f')] +$LeftSideShadow, +# Set the left_flip_width of OBSAlphaGamingBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] [Single] -$RandActivationF, -# Set the loops of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('loops')] -[Int32] -$Loops, -# Set the local_time of OBSMatrixShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] +$LeftFlipWidth, +# Set the left_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] [Single] -$LocalTime, -# Set the mouse of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('mouse')] -[Single[]] -$Mouse, -# Set the Invert_Direction of OBSMatrixShader -[Alias('Invert_Direction')] -[ComponentModel.DefaultBindingProperty('Invert_Direction')] -[Management.Automation.SwitchParameter] -$InvertDirection, -# Set the lumaMin of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMin')] +$LeftFlipShadow, +# Set the right_side_width of OBSAlphaGamingBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] [Single] -$LumaMin, -# Set the lumaMinSmooth of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +$RightSideWidth, +# Set the right_side_size of OBSAlphaGamingBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] [Single] -$LumaMinSmooth, -# Set the Ratio of OBSMatrixShader -[ComponentModel.DefaultBindingProperty('Ratio')] +$RightSideSize, +# Set the right_side_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] [Single] -$Ratio, -# Set the Alpha_Percentage of OBSMatrixShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +$RightSideShadow, +# Set the right_flip_width of OBSAlphaGamingBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] [Single] -$AlphaPercentage, -# Set the Apply_To_Alpha_Layer of OBSMatrixShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, +$RightFlipWidth, +# Set the right_flip_shadow of OBSAlphaGamingBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28565,228 +19943,127 @@ $UseShaderTime process { -$shaderName = 'matrix' -$ShaderNoun = 'OBSMatrixShader' +$shaderName = 'alpha-gaming-bent-camera' +$ShaderNoun = 'OBSAlphaGamingBentCameraShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 -// https://github.com/Oncorporation/obs-shaderfilter -// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated - -#define vec2 float2 -#define vec3 float3 -#define vec4 float4 -#define ivec2 int2 -#define ivec3 int3 -#define ivec4 int4 -#define mat2 float2x2 -#define mat3 float3x3 -#define mat4 float4x4 -#define fract frac -#define mix lerp -#define iTime float - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_size; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float rand_instance_f; -uniform float rand_activation_f; -uniform int loops; -uniform float local_time; - - -uniform float2 mouse< - string label = "Virtual Mouse Coordinates"; - string widget_type = "slider"; - float2 minimum = {0, 0}; - float2 maximum = {100., 100.}; - float2 scale = {.01, .01}; - float2 step = {.01, .01}; -> = {0., 0.}; - - -int2 iMouse() { - return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); -} - -sampler_state textureSampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -/* ps start - -*/ - -uniform bool Invert_Direction< - string label = "Invert Direction"; -> = true; - -uniform float lumaMin< - string label = "Luma Min"; +uniform float left_side_width< + string label = "Left side width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float lumaMinSmooth< - string label = "Luma Min Smooth"; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.01; -uniform float Ratio< - string label = "Ratio"; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.01; -> = 4.0; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.01; -> = 100; // -uniform bool Apply_To_Alpha_Layer = true; - -#define PI2 6.28318530718 -#define PI 3.1416 - - -float vorocloud(float2 p){ - float f = 0.0; - float flow = 1.0; - float time = elapsed_time; - if(Invert_Direction){ - flow *= -1; - } - /* - //periodically stop - if (loops % 16 >= 8.0) - { - time = local_time - elapsed_time; - } - */ - - float r = clamp(Ratio,-50,50); - float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); - p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); - - float2 pts[4]; - - pts[0] = float2(0.5, 0.6); - pts[1] = float2(-0.4, 0.4); - pts[2] = float2(0.2, -0.7); - pts[3] = float2(-0.3, -0.4); - - float d = 5.0; - - for(int i = 0; i < 4; i++){ - pts[i].x += 0.03 * cos(float(i)) + p.x; - pts[i].y += 0.03 * sin(float(i)) + p.y; - d = min(d, distance(pts[i], pp)); - } - - f = 2.0 * pow(1.0 - 0.3 * d, 13.0); - - f = min(f, 1.0); - - return f; -} - -vec4 scene(float2 UV){ - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - - float x = UV.x; - float y = UV.y; - - float2 p = float2(x, y) - 0.5; - - vec4 col = vec4(0.0,0.0,0.0,0.0); - col.g += 0.02; - - float v = vorocloud(p); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.6 * v; - col.b += 0.5 * pow(v, 5.0); - - - v = vorocloud(p * 2.0); - v = 0.2 * floor(v * 5.0); - - col.r += 0.1 * v; - col.g += 0.2 * v; - col.b += 0.01 * pow(v, 5.0); - - col.a = 1.0; - float luma = dot(col.rgb,float3(0.299,0.587,0.114)); - float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); - col.a = clamp(luma_min,0.0,1.0); - - float4 original_color = image.Sample(textureSampler, UV); - - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - if (Apply_To_Alpha_Layer == false) - original_color.a = alpha; - - col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider - col = lerp(original_color, col, col.a); //remove black background color - } - else - { - col.a = original_color.a; - } +> = 0.05; +uniform float left_flip_shadow< + string label = "Left flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; - return col; -} +uniform float right_side_width< + string label = "Right side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; -void mainImage( out vec4 fragColor, in vec2 fragCoord ) +float4 mainImage(VertData v_in) : TARGET { - vec2 uv = fragCoord.xy / uv_size; - fragColor = scene(uv); -} - -/*ps end*/ - -struct VertFragData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertFragData VSDefault(VertFragData vtx) { - vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); - return vtx; -} - -float4 PSDefault(VertFragData vtx) : TARGET { - float4 col = float4(1., 1., 1., 1.); - mainImage(col, vtx.uv * uv_size); - return col; -} + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; + } -technique Draw -{ - pass - { - vertex_shader = VSDefault(vtx); - pixel_shader = PSDefault(vtx); - } + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; + } + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -28884,19 +20161,57 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMotionBlurShader { +function Get-OBSAnimatedPathShader { -[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] +[Alias('Set-OBSAnimatedPathShader','Add-OBSAnimatedPathShader')] param( -# Set the previous_output of OBSMotionBlurShader -[Alias('previous_output')] -[ComponentModel.DefaultBindingProperty('previous_output')] +# Set the ViewProj of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$PreviousOutput, -# Set the strength of OBSMotionBlurShader -[ComponentModel.DefaultBindingProperty('strength')] +$Image, +# Set the elapsed_time of OBSAnimatedPathShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$Strength, +$ElapsedTime, +# Set the uv_offset of OBSAnimatedPathShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedPathShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedPathShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedPathShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the speed_percent of OBSAnimatedPathShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the path_map of OBSAnimatedPathShader +[Alias('path_map')] +[ComponentModel.DefaultBindingProperty('path_map')] +[String] +$PathMap, +# Set the reverse of OBSAnimatedPathShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -28927,23 +20242,100 @@ $UseShaderTime process { -$shaderName = 'motion_blur' -$ShaderNoun = 'OBSMotionBlurShader' +$shaderName = 'animated_path' +$ShaderNoun = 'OBSAnimatedPathShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d previous_output; -uniform float strength< - string label = "strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +// Path effect By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; + +uniform int speed_percent = 100; +uniform texture2d path_map; +uniform bool reverse = false; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} + +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + float3 pos = v_in.pos.xyz; + float3 current_pos; + float speed = speed_percent * 0.01; + //vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float t = 1.0 + sin(elapsed_time * speed) ; + // combine luma texture and user defined shine color + float luma = path_map.Sample(textureSampler, v_in.uv).x; + if (reverse) + { + luma = 1.0 - luma; + } + + float time = lerp(0.0f, 1.0f , t - 1.0); + + // set current position in time + current_pos.x = 0; + current_pos.y = 0; + + + float2 offset = uv_offset; + if (speed == 0.0f) + { + offset.x = 0.0f; + offset.y = 0.0f; + } + else + { + offset.x = uv_offset.x + time * luma; + offset.y = uv_offset.y + time * luma; + } + + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} float4 mainImage(VertData v_in) : TARGET { - return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); + return image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29041,56 +20433,312 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMultiplyShader { +function Get-OBSAnimatedTextureShader { -[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] +[Alias('Set-OBSAnimatedTextureShader','Add-OBSAnimatedTextureShader')] param( -# Set the other_image of OBSMultiplyShader -[Alias('other_image')] -[ComponentModel.DefaultBindingProperty('other_image')] +# Set the ViewProj of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('image')] [String] -$OtherImage, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] +$Image, +# Set the elapsed_time of OBSAnimatedTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSAnimatedTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAnimatedTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAnimatedTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAnimatedTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSAnimatedTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] +$Notes, +# Set the Animation_Image of OBSAnimatedTextureShader +[Alias('Animation_Image')] +[ComponentModel.DefaultBindingProperty('Animation_Image')] [String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] +$AnimationImage, +# Set the Colorization_Image of OBSAnimatedTextureShader +[Alias('Colorization_Image')] +[ComponentModel.DefaultBindingProperty('Colorization_Image')] [String] -$ShaderText, -# If set, will force the recreation of a shader that already exists +$ColorizationImage, +# Set the reverse of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('reverse')] [Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +$Reverse, +# Set the bounce of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('bounce')] [Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +$Bounce, +# Set the center_animation of OBSAnimatedTextureShader +[Alias('center_animation')] +[ComponentModel.DefaultBindingProperty('center_animation')] [Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +$CenterAnimation, +# Set the polar_animation of OBSAnimatedTextureShader +[Alias('polar_animation')] +[ComponentModel.DefaultBindingProperty('polar_animation')] [Management.Automation.SwitchParameter] -$UseShaderTime +$PolarAnimation, +# Set the polar_angle of OBSAnimatedTextureShader +[Alias('polar_angle')] +[ComponentModel.DefaultBindingProperty('polar_angle')] +[Single] +$PolarAngle, +# Set the polar_height of OBSAnimatedTextureShader +[Alias('polar_height')] +[ComponentModel.DefaultBindingProperty('polar_height')] +[Single] +$PolarHeight, +# Set the speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('speed_horizontal_percent')] +[Single] +$SpeedHorizontalPercent, +# Set the speed_vertical_percent of OBSAnimatedTextureShader +[Alias('speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('speed_vertical_percent')] +[Single] +$SpeedVerticalPercent, +# Set the tint_speed_horizontal_percent of OBSAnimatedTextureShader +[Alias('tint_speed_horizontal_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_horizontal_percent')] +[Single] +$TintSpeedHorizontalPercent, +# Set the tint_speed_vertical_percent of OBSAnimatedTextureShader +[Alias('tint_speed_vertical_percent')] +[ComponentModel.DefaultBindingProperty('tint_speed_vertical_percent')] +[Single] +$TintSpeedVerticalPercent, +# Set the Alpha of OBSAnimatedTextureShader +[ComponentModel.DefaultBindingProperty('Alpha')] +[Single] +$Alpha, +# Set the Use_Animation_Image_Color of OBSAnimatedTextureShader +[Alias('Use_Animation_Image_Color')] +[ComponentModel.DefaultBindingProperty('Use_Animation_Image_Color')] +[Management.Automation.SwitchParameter] +$UseAnimationImageColor, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { -$shaderName = 'multiply' -$ShaderNoun = 'OBSMultiplyShader' +$shaderName = 'animated_texture' +$ShaderNoun = 'OBSAnimatedTextureShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d other_image; +// Animated Texture By Charles Fettinger (https://github.com/Oncorporation) 3/2020 +// Animates a texture with polar sizing and color options +// for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Q-mii & Exeldro February 24, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes; + +uniform texture2d Animation_Image; +uniform texture2d Colorization_Image; +uniform bool reverse = false; +uniform bool bounce = false; +uniform bool center_animation = true; +uniform bool polar_animation = true; +uniform float polar_angle = 90.0; +uniform float polar_height = 1.0; +uniform float speed_horizontal_percent = 50; +uniform float speed_vertical_percent = 5; +uniform float tint_speed_horizontal_percent = 50; +uniform float tint_speed_vertical_percent = 5; +uniform float Alpha = 1.0; +uniform bool Use_Animation_Image_Color = true; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +float4 convert_pmalpha(float4 color) +{ + float4 ret = color; + if (color.a >= 0.001) + ret.xyz /= color.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} + +float2 time(float2 speed_dir) +{ + float PI = 3.1415926535897932384626433832795; //acos(-1); + + float2 t = (elapsed_time * speed_dir) ; + if (bounce) + { + // coordinates moved from -1.0 to 1.0 to 0.0 to 2.0 then modified to fit screen + t.x = sin(elapsed_time * speed_dir.x * PI * 0.6667) + 1.0; + t.y = cos(elapsed_time * speed_dir.y * PI) + 1.0; + t *= -0.5; + } + + if (reverse) + t = t * -1; + return t; +} + +VertData mainTransform(VertData v_in) +{ + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + + VertData vert_out; + //float2 direction = abs(sin((elapsed_time - 0.001) * speed_dir)); + + float2 offset = uv_offset; + + if (center_animation) + { + vert_out.uv = v_in.uv - 0.5f; + } + else + { + offset += time(speed_dir); + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + offset; + } + + return vert_out; +} + float4 mainImage(VertData v_in) : TARGET { - float4 other = other_image.Sample(textureSampler, v_in.uv); - float4 base = image.Sample(textureSampler, v_in.uv); - return base * other; + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + + float2 speed_dir = float2(speed_horizontal_percent * 0.01, speed_vertical_percent * 0.01); + float2 tint_speed_dir = float2(tint_speed_horizontal_percent * 0.01, tint_speed_vertical_percent * 0.01); + + //compensate for background vertex shader values + float2 background_offset = float2(-.5,-.5); + if (!center_animation) + background_offset = time(speed_dir); + float4 rgba = image.Sample(textureSampler, v_in.uv - background_offset); //float4(0.0,0.0,0.0,0.01); + + // Convert our texture coordinates to polar form: + if (polar_animation) { + + float2 polar = float2( + atan2(v_in.uv.y, v_in.uv.x) / (polar_angle * PI180th * 4), // angle + log(dot(v_in.uv, v_in.uv)) * -1 * (polar_height * PI180th * PI) // log-radius + ); + + // Check how much our texture sampling point changes between + // neighbouring pixels to the sides (ddx) and above/below (ddy) + ///float4 gradient = float4(ddx(polar), ddy(polar)); + + // If our angle wraps around between adjacent samples, + // discard one full rotation from its value and keep the fraction. + ///gradient.xz = frac(gradient.xz + 1.5f) - 0.5f; + + float2 tintUVs = polar * 4; + tintUVs += time(tint_speed_dir); + + // Apply texture scale + polar *= 4; + // Scroll the texture over time. + polar += time(speed_dir); + float4 animation = Animation_Image.Sample(textureSampler, frac(polar)); + + + float keyAmount = distance(animation.rgb,float3(0.0,0.0,0.0)); + float intensity = dot(animation.rgb ,float3(0.299,0.587,0.114)); + //animation.a = clamp((intensity),0.0,1.0); + if (Use_Animation_Image_Color) + { + animation.rgb *= Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + else + { + animation.rgb = Colorization_Image.Sample(textureSampler, frac(tintUVs)).rgb; + } + //if (keyAmount > 0.5f) + rgba = lerp(rgba, animation, animation.a * Alpha); + } + + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } ' @@ -29190,99 +20838,32 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSNightSkyShader { +function Get-OBSAsciiShader { -[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] +[Alias('Set-OBSAsciiShader','Add-OBSAsciiShader')] param( -# Set the speed of OBSNightSkyShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the Include_Clouds of OBSNightSkyShader -[Alias('Include_Clouds')] -[ComponentModel.DefaultBindingProperty('Include_Clouds')] -[Management.Automation.SwitchParameter] -$IncludeClouds, -# Set the Include_Moon of OBSNightSkyShader -[Alias('Include_Moon')] -[ComponentModel.DefaultBindingProperty('Include_Moon')] -[Management.Automation.SwitchParameter] -$IncludeMoon, -# Set the center_width_percentage of OBSNightSkyShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSNightSkyShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] +# Set the scale of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('scale')] [Int32] -$CenterHeightPercentage, -# Set the Alpha_Percentage of OBSNightSkyShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSNightSkyShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSNightSkyShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +$Scale, +# Set the base_color of OBSAsciiShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] +[String] +$BaseColor, +# Set the monochrome of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('monochrome')] [Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the number_stars of OBSNightSkyShader -[Alias('number_stars')] -[ComponentModel.DefaultBindingProperty('number_stars')] +$Monochrome, +# Set the character_set of OBSAsciiShader +[Alias('character_set')] +[ComponentModel.DefaultBindingProperty('character_set')] [Int32] -$NumberStars, -# Set the SKY_COLOR of OBSNightSkyShader -[Alias('SKY_COLOR')] -[ComponentModel.DefaultBindingProperty('SKY_COLOR')] -[String] -$SKYCOLOR, -# Set the STAR_COLOR of OBSNightSkyShader -[Alias('STAR_COLOR')] -[ComponentModel.DefaultBindingProperty('STAR_COLOR')] -[String] -$STARCOLOR, -# Set the LIGHT_SKY of OBSNightSkyShader -[Alias('LIGHT_SKY')] -[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] -[String] -$LIGHTSKY, -# Set the SKY_LIGHTNESS of OBSNightSkyShader -[Alias('SKY_LIGHTNESS')] -[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] -[Single] -$SKYLIGHTNESS, -# Set the MOON_COLOR of OBSNightSkyShader -[Alias('MOON_COLOR')] -[ComponentModel.DefaultBindingProperty('MOON_COLOR')] +$CharacterSet, +# Set the note of OBSAsciiShader +[ComponentModel.DefaultBindingProperty('note')] [String] -$MOONCOLOR, -# Set the moon_size of OBSNightSkyShader -[Alias('moon_size')] -[ComponentModel.DefaultBindingProperty('moon_size')] -[Single] -$MoonSize, -# Set the moon_bump_size of OBSNightSkyShader -[Alias('moon_bump_size')] -[ComponentModel.DefaultBindingProperty('moon_bump_size')] -[Single] -$MoonBumpSize, -# Set the Moon_Position_x of OBSNightSkyShader -[Alias('Moon_Position_x')] -[ComponentModel.DefaultBindingProperty('Moon_Position_x')] -[Single] -$MoonPositionX, -# Set the Moon_Position_y of OBSNightSkyShader -[Alias('Moon_Position_y')] -[ComponentModel.DefaultBindingProperty('Moon_Position_y')] -[Single] -$MoonPositionY, +$Note, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29313,297 +20894,109 @@ $UseShaderTime process { -$shaderName = 'night_sky' -$ShaderNoun = 'OBSNightSkyShader' +$shaderName = 'ascii' +$ShaderNoun = 'OBSAsciiShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 +// ASCII shader for use with obs-shaderfilter 7/2020 v1.0 // https://github.com/Oncorporation/obs-shaderfilter -//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 20.0; -uniform bool Include_Clouds = true; -uniform bool Include_Moon = true; -uniform int center_width_percentage< - string label = "Center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "Center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 95.0; // -uniform bool Apply_To_Image = false; -uniform bool Replace_Image_Color = false; -uniform int number_stars< - string label = "Number stars"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; // +// Based on the following shaders: +// https://www.shadertoy.com/view/3dtXD8 - Created by DSWebber in 2019-10-24 +// https://www.shadertoy.com/view/lssGDj - Created by movAX13h in 2013-09-22 -uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; -uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; -uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; -uniform float SKY_LIGHTNESS< - string label = "SKY LIGHTNESS"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = .3; +// Modifications of original shaders include: +// - Porting from GLSL to HLSL +// - Combining characters sets from both source shaders +// - Adding support for parameters from OBS for monochrome rendering, scaling and dynamic character set +// +// Add Additional Characters with this tool: http://thrill-project.com/archiv/coding/bitmap/ +// converts a bitmap into int then decodes it to look like text - // Moon -uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; -uniform float moon_size< - string label = "Moon size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.18; -uniform float moon_bump_size< - string label = "Moon bump size"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.14; -uniform float Moon_Position_x< - string label = "Moon Position x"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.6; -uniform float Moon_Position_y< - string label = "Moon Position y"; +uniform int scale< + string label = "Scale"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = -0.3; - -#define PI 3.1416 + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; // Size of characters +uniform float4 base_color< + string label = "Base color"; +> = {0.0,1.0,0.0,1.0}; // Monochrome base color +uniform bool monochrome< + string label = "Monochrome"; +> = false; +uniform int character_set< + string label = "Character set"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Large set of non-letters"; + int option_1_value = 1; + string option_1_label = "Small set of capital letters"; +> = 0; +uniform string note< + string widget_type = "info"; +> = "Base color is used as monochrome base color."; -//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU -float noise11(float p) { - return frac(sin(p*633.1847) * 9827.95); -} - -float noise21(float2 p) { - return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); +float character(int n, float2 p) +{ + p = floor(p*float2(4.0, 4.0) + 2.5); + if (clamp(p.x, 0.0, 4.0) == p.x) + { + if (clamp(p.y, 0.0, 4.0) == p.y) + { + int a = int(round(p.x) + 5.0 * round(p.y)); + if (((n >> a) & 1) == 1) return 1.0; + } + } + return 0.0; } -float2 noise22(float2 p) { - return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x/y); } -//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise -float cosineInterpolation(float a, float b, float x) { - float ft = x * PI; - float f = (1. - cos(ft)) * .5; - return a * (1. - f) + b * f; -} +float4 mainImage( VertData v_in ) : TARGET +{ + float2 iResolution = uv_size; + float2 pix = v_in.uv * iResolution; + float4 c = image.Sample(textureSampler, floor(pix/float2(scale*8.0,scale*8.0))*float2(scale*8.0,scale*8.0)/iResolution.xy); -float smoothNoise11(float p, float dist) { - float prev = noise11(p-dist); - float next = noise11(p+dist); - - return cosineInterpolation(prev, next, .5); -} + float gray = 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + + int n; + int charset = clamp(character_set, 0, 1); -float smoothNoise21(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - //smoothstep function: maybe change it later! - lv = lv*lv*(3.-2.*lv); - - float bl = noise21(id); - float br = noise21(id+float2(1.,0.)); - float b = lerp(bl, br, lv.x); - - float tl = noise21(id+float2(0.,1.)); - float tr = noise21(id+float2(1.,1.)); - float t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} - -float2 smoothNoise22(float2 uv, float cells) { - float2 lv = frac(uv*cells); - float2 id = floor(uv*cells); - - lv = lv*lv*(3.-2.*lv); - - float2 bl = noise22(id); - float2 br = noise22(id+float2(1.,0.)); - float2 b = lerp(bl, br, lv.x); - - float2 tl = noise22(id+float2(0.,1.)); - float2 tr = noise22(id+float2(1.,1.)); - float2 t = lerp(tl, tr, lv.x); - - return lerp(b, t, lv.y); -} - -float valueNoise11(float p) { - float c = smoothNoise11(p, 0.5); - c += smoothNoise11(p, 0.25)*.5; - c += smoothNoise11(p, 0.125)*.25; - c += smoothNoise11(p, 0.0625)*.125; - - return c /= .875; -} - -float valueNoise21(float2 uv) { - float c = smoothNoise21(uv, 4.); - c += smoothNoise21(uv, 8.)*.5; - c += smoothNoise21(uv, 16.)*.25; - c += smoothNoise21(uv, 32.)*.125; - c += smoothNoise21(uv, 64.)*.0625; - - return c /= .9375; -} - -float2 valueNoise22(float2 uv) { - float2 c = smoothNoise22(uv, 4.); - c += smoothNoise22(uv, 8.)*.5; - c += smoothNoise22(uv, 16.)*.25; - c += smoothNoise22(uv, 32.)*.125; - c += smoothNoise22(uv, 64.)*.0625; - - return c /= .9375; -} - -float3 points(float2 p, float2 uv, float3 color, float size, float blur) { - float dist = distance(p, uv); - return color*smoothstep(size, size*(0.999-blur), dist); -} - -float mapInterval(float x, float a, float b, float c, float d) { - return (x-a)/(b-a) * (d-c) + c; -} - -float blink(float time, float timeInterval) { - float halfInterval = timeInterval / 2.0; - //Get relative position in the bucket - float p = fmod(time, timeInterval); - - - if (p <= timeInterval / 2.) { - return smoothstep(0., 1., p/halfInterval); - } else { - return smoothstep(1., 0., (p-halfInterval)/halfInterval); - } -} - -float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { - float dist = distance(p, uv); - float3 BumpSamples = float3(0.,0.,0.); - - if (dist < radius) { - float bumps = (1.-valueNoise21(uv*spin))*.1; - BumpSamples = float3(bumps, bumps, bumps); - } - return BumpSamples; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba;// = image.Sample(textureSampler, v_in.uv); - float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st; - - // Normalized pixel coordinates (from 0 to 1) - float2 uv = v_in.uv; - float2 ouv = uv; - uv -= .5; - uv.x *= uv_size.x/uv_size.y; - - float2 seed = toCenter / uv_size.xy; - - float time = elapsed_time + seed.x*speed; - - //float3 col = float3(0.0); - //float m = valueNoise21(uv); - float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); - - col *= SKY_LIGHTNESS - (1.-ouv.y); - - //Add clouds - if (Include_Clouds) + if (charset==0) { - float2 timeUv = uv; - timeUv.x += time*.1; - timeUv.y += valueNoise11(timeUv.x+.352)*.01; - float cloud = valueNoise21(timeUv); - col += cloud*.1; + if (gray <= 0.2) n = 4096; // . + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 332772; // * + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 23385164; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13199452; // @ + if (gray > 0.8) n = 11512810; // # } - - //Add stars in the top part of the scene - float timeInterval = speed *.5; //5.0 - float timeBucket = floor(time / timeInterval); - - float2 moonPosition = float2(-1000, -1000); - if (Include_Moon) - { - moonPosition = float2(Moon_Position_x, Moon_Position_y); - col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); - // Moon bumps - col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); + else if (charset==1) + { + if (gray <= 0.1) n = 0; + if (gray > 0.1) n = 9616687; // R + if (gray > 0.3) n = 32012382; // S + if (gray > 0.5) n = 16303663; // D + if (gray > 0.7) n = 15255086; // O + if (gray > 0.8) n = 16301615; // B } - for (float i = 0.; i < clamp(number_stars,0,100); i++) { - float2 starPosition = float2(i/10., i/10.); - - starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); - starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); - - float starIntensity = blink(time+ (rand_f * i), timeInterval ); - //Hide stars that are behind the moon - if (distance(starPosition, moonPosition) > moon_size) { - col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; - col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; - } + float2 p = mod(pix/float2(scale*4.0,scale*4.0),float2(2.0,2.0)) - float2(1.0,1.0); + + if (monochrome) + { + c.rgb = base_color.rgb; } - //col = float3(blink(time, timeInterval)); - rgba = float4(col,alpha); - - // Output to screen - if (Apply_To_Image) - { - float4 color = image.Sample(textureSampler, v_in.uv); - float4 original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,alpha); - - } - return rgba; + c = c*character(n, p); + + return c; } ' @@ -29703,31 +21096,56 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSNoiseShader { +function Get-OBSAspectRatioShader { -[Alias('Set-OBSNoiseShader','Add-OBSNoiseShader')] +[Alias('Set-OBSAspectRatioShader','Add-OBSAspectRatioShader')] param( -# Set the speed of OBSNoiseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the scale of OBSNoiseShader -[ComponentModel.DefaultBindingProperty('scale')] +# Set the ViewProj of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSAspectRatioShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$Scale, -# Set the noiseLevel of OBSNoiseShader -[ComponentModel.DefaultBindingProperty('noiseLevel')] +$ElapsedTime, +# Set the uv_offset of OBSAspectRatioShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSAspectRatioShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSAspectRatioShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSAspectRatioShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$NoiseLevel, -# Set the monochromatic of OBSNoiseShader -[ComponentModel.DefaultBindingProperty('monochromatic')] -[Management.Automation.SwitchParameter] -$Monochromatic, -# Set the use_rand of OBSNoiseShader -[Alias('use_rand')] -[ComponentModel.DefaultBindingProperty('use_rand')] -[Management.Automation.SwitchParameter] -$UseRand, +$RandF, +# Set the uv_size of OBSAspectRatioShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the borderColor of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('borderColor')] +[String] +$BorderColor, +# Set the notes of OBSAspectRatioShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29758,66 +21176,103 @@ $UseShaderTime process { -$shaderName = 'noise' -$ShaderNoun = 'OBSNoiseShader' +$shaderName = 'aspect_ratio' +$ShaderNoun = 'OBSAspectRatioShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100; - float step = 0.1; -> = 1; -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 10; - float step = 0.0001; -> = 6; -uniform float noiseLevel< - string label = "Noise Level"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 1; - float step = 0.001; -> = 1; -uniform bool monochromatic = false; -uniform bool use_rand = false; +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - DO NOT USE THIS IT WAS NEVER COMPLETED +uniform float4x4 ViewProj; +uniform texture2d image; -float rand(float2 st) +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; + + +// variables +uniform float4 borderColor = {0,0,0,0}; +float targetaspect = 1.7777777777777777777777f; //16.0f / 9.0f; +uniform string notes; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) { - return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123); -} + VertData vert_out; + + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; -float4 mainImage(VertData v_in) : TARGET -{ - float time = rand_activation_f + (speed / 60) * elapsed_time * 0.00001; + float2 hw = uv_scale; + // determine the game window''s current aspect ratio + float windowaspect = hw.x / hw.y; - if (use_rand) { - time = rand_f; - } + // current viewport height should be scaled by this amount + float scaleheight = windowaspect / targetaspect; - float4 x; - if (monochromatic) { - x = rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + scale); - } else { - x = float4( - rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 1 * scale), - rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 2 * scale), - rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 3 * scale), - 1 - ); - } + // if scaled height is less than current height, add letterbox + if (scaleheight < 1.0f) + { + Rect rect = camera.rect; - float4 rgba = image.Sample(textureSampler, v_in.uv); + rect.width = 1.0f; + rect.height = scaleheight; + rect.x = 0; + rect.y = (1.0f - scaleheight) / 2.0f; - float3 output = lerp(rgba, x, noiseLevel); + camera.rect = rect; + } + else // add pillarbox + { + float scalewidth = 1.0f / scaleheight; - return float4(output.r, output.g, output.b, rgba.a); + Rect rect = camera.rect; + + rect.width = scalewidth; + rect.height = 1.0f; + rect.x = (1.0f - scalewidth) / 2.0f; + rect.y = 0; + + camera.rect = rect; + } + return vert_out; +} + +float4 mainImage(VertData v_in) : TARGET +{ + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; + } + else + { + return image.Sample(textureSampler, v_in.uv); + } +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -29915,34 +21370,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSNormalMapShader { +function Get-OBSAudioShader { -[Alias('Set-OBSNormalMapShader','Add-OBSNormalMapShader')] +[Alias('Set-OBSAudioShader','Add-OBSAudioShader')] param( -# Set the strength of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('strength')] +# Set the audio_peak of OBSAudioShader +[Alias('audio_peak')] +[ComponentModel.DefaultBindingProperty('audio_peak')] [Single] -$Strength, -# Set the offsetHeight of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('offsetHeight')] -[Management.Automation.SwitchParameter] -$OffsetHeight, -# Set the invertR of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('invertR')] -[Management.Automation.SwitchParameter] -$InvertR, -# Set the invertG of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('invertG')] -[Management.Automation.SwitchParameter] -$InvertG, -# Set the invertH of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('invertH')] -[Management.Automation.SwitchParameter] -$InvertH, -# Set the type of OBSNormalMapShader -[ComponentModel.DefaultBindingProperty('type')] -[Int32] -$Type, +$AudioPeak, +# Set the audio_magnitude of OBSAudioShader +[Alias('audio_magnitude')] +[ComponentModel.DefaultBindingProperty('audio_magnitude')] +[Single] +$AudioMagnitude, +# Set the intensity of OBSAudioShader +[ComponentModel.DefaultBindingProperty('intensity')] +[Single] +$Intensity, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -29973,106 +21418,52 @@ $UseShaderTime process { -$shaderName = 'normal_map' -$ShaderNoun = 'OBSNormalMapShader' +$shaderName = 'audio' +$ShaderNoun = 'OBSAudioShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Normal map shader based on cpetry''s NormalMap-Online website -// https://github.com/cpetry/NormalMap-Online/blob/gh-pages/javascripts/shader/NormalMapShader.js +// Audio shader example showing the difference between audio_peak and audio_magnitude. +// Left half uses audio_peak (red), right half uses audio_magnitude (blue). +uniform float audio_peak; +uniform float audio_magnitude; -uniform float strength< - string label = "Strength"; +uniform float intensity < + string label = "Audio intensity"; string widget_type = "slider"; - float minimum = 0.001; - float maximum = 10; - float step = 0.001; -> = 1; - -uniform bool offsetHeight = true; -uniform bool invertR = false; -uniform bool invertG = false; -uniform bool invertH = false; - -uniform int type< - string label = "Filter Type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Sobel"; - int option_1_value = 1; - string option_1_label = "Scharr"; -> = 0; + float minimum = 0.1; + float maximum = 3.0; + float step = 0.1; +> = 1.0; -float4 mainImage( VertData v_in ) : TARGET { - float2 step = float2(1.0, 1.0) / uv_size; - float2 vUv = v_in.uv; +float4 mainImage(VertData v_in) : TARGET { + float4 color = image.Sample(textureSampler, v_in.uv); + + // Split screen based on UV coordinate + if (v_in.uv.x < 0.5) { + // Left half: audio_peak (instantaneous spikes, more reactive) + // Tint with red to show peak activity. + float peak_strength = audio_peak * intensity; + float3 peak_color = color.rgb + float3(peak_strength, 0, 0); + return float4(peak_color, color.a); + } else { + // Right half: audio_magnitude (RMS/averaged levels, smoother) + // Tint with blue to show magnitude activity. + float mag_strength = audio_magnitude * intensity; + float3 mag_color = color.rgb + float3(0, 0, mag_strength); + return float4(mag_color, color.a); + } +} - float dz = 1 / strength; - float dz2 = dz * dz; +/* +EXPLANATION: +- audio_peak: Shows instantaneous maximum levels, very responsive to drums/percussion. +- audio_magnitude: Shows RMS (Root Mean Square) levels, smoother and represents sustained audio. - float2 tlv = float2(vUv.x - step.x, vUv.y + step.y); - float2 lv = float2(vUv.x - step.x, vUv.y ); - float2 blv = float2(vUv.x - step.x, vUv.y - step.y); - float2 tv = float2(vUv.x , vUv.y + step.y); - float2 bv = float2(vUv.x , vUv.y - step.y); - float2 trv = float2(vUv.x + step.x, vUv.y + step.y); - float2 rv = float2(vUv.x + step.x, vUv.y ); - float2 brv = float2(vUv.x + step.x, vUv.y - step.y); - - tlv = float2(tlv.x >= 0.0 ? tlv.x : (1.0 + tlv.x), tlv.y >= 0.0 ? tlv.y : (1.0 + tlv.y)); - tlv = float2(tlv.x < 1.0 ? tlv.x : (tlv.x - 1.0 ), tlv.y < 1.0 ? tlv.y : (tlv.y - 1.0 )); - lv = float2( lv.x >= 0.0 ? lv.x : (1.0 + lv.x), lv.y >= 0.0 ? lv.y : (1.0 + lv.y)); - lv = float2( lv.x < 1.0 ? lv.x : ( lv.x - 1.0 ), lv.y < 1.0 ? lv.y : ( lv.y - 1.0 )); - blv = float2(blv.x >= 0.0 ? blv.x : (1.0 + blv.x), blv.y >= 0.0 ? blv.y : (1.0 + blv.y)); - blv = float2(blv.x < 1.0 ? blv.x : (blv.x - 1.0 ), blv.y < 1.0 ? blv.y : (blv.y - 1.0 )); - tv = float2( tv.x >= 0.0 ? tv.x : (1.0 + tv.x), tv.y >= 0.0 ? tv.y : (1.0 + tv.y)); - tv = float2( tv.x < 1.0 ? tv.x : ( tv.x - 1.0 ), tv.y < 1.0 ? tv.y : ( tv.y - 1.0 )); - bv = float2( bv.x >= 0.0 ? bv.x : (1.0 + bv.x), bv.y >= 0.0 ? bv.y : (1.0 + bv.y)); - bv = float2( bv.x < 1.0 ? bv.x : ( bv.x - 1.0 ), bv.y < 1.0 ? bv.y : ( bv.y - 1.0 )); - trv = float2(trv.x >= 0.0 ? trv.x : (1.0 + trv.x), trv.y >= 0.0 ? trv.y : (1.0 + trv.y)); - trv = float2(trv.x < 1.0 ? trv.x : (trv.x - 1.0 ), trv.y < 1.0 ? trv.y : (trv.y - 1.0 )); - rv = float2( rv.x >= 0.0 ? rv.x : (1.0 + rv.x), rv.y >= 0.0 ? rv.y : (1.0 + rv.y)); - rv = float2( rv.x < 1.0 ? rv.x : ( rv.x - 1.0 ), rv.y < 1.0 ? rv.y : ( rv.y - 1.0 )); - brv = float2(brv.x >= 0.0 ? brv.x : (1.0 + brv.x), brv.y >= 0.0 ? brv.y : (1.0 + brv.y)); - brv = float2(brv.x < 1.0 ? brv.x : (brv.x - 1.0 ), brv.y < 1.0 ? brv.y : (brv.y - 1.0 )); - - float tl = image.Sample(textureSampler, tlv).r; - float l = image.Sample(textureSampler, lv ).r; - float bl = image.Sample(textureSampler, blv).r; - float t = image.Sample(textureSampler, tv ).r; - float b = image.Sample(textureSampler, bv ).r; - float tr = image.Sample(textureSampler, trv).r; - float r = image.Sample(textureSampler, rv ).r; - float br = image.Sample(textureSampler, brv).r; - - float dx = 0.0; - float dy = 0.0; - - if(type == 0) { // Sobel - dx = tl + l*2.0 + bl - tr - r*2.0 - br; - dy = tl + t*2.0 + tr - bl - b*2.0 - br; - } - else { // Scharr - dx = tl*3.0 + l*10.0 + bl*3.0 - tr*3.0 - r*10.0 - br*3.0; - dy = tl*3.0 + t*10.0 + tr*3.0 - bl*3.0 - b*10.0 - br*3.0; - } - - float invH = invertH ? -1. : 1.; - float invR = invertR ? -1. : 1.; - float invG = invertG ? -1. : 1.; - - float4 normal = float4( - float3(dx * invR * invH, dy * invG * invH, dz), - image.Sample(textureSampler, vUv).a - ); - - l = sqrt((dx * dx) + (dy * dy) + dz2); - - if (offsetHeight) { - return float4(normal.xy / l * 0.5 + 0.5, normal.zw); - } - - return float4(normal.xyz / l * 0.5 + 0.5, normal.w); -} +TYPICAL BEHAVIOR: +- With music containing drums: Left side (peak) will flash more dramatically on beats. +- With sustained tones: Right side (magnitude) will show more consistent levels. +- Peak reacts faster to sudden sounds, magnitude is more stable for smooth effects. +*/ ' } @@ -30171,14 +21562,78 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOpacityShader { +function Get-OBSBackgroundRemovalShader { -[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] +[Alias('Set-OBSBackgroundRemovalShader','Add-OBSBackgroundRemovalShader')] param( -# Set the Opacity of OBSOpacityShader -[ComponentModel.DefaultBindingProperty('Opacity')] +# Set the ViewProj of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSBackgroundRemovalShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSBackgroundRemovalShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSBackgroundRemovalShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSBackgroundRemovalShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSBackgroundRemovalShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSBackgroundRemovalShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the target of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('target')] +[String] +$Target, +# Set the color of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the opacity of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('opacity')] [Single] $Opacity, +# Set the invert of OBSBackgroundRemovalShader +[ComponentModel.DefaultBindingProperty('invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the Convert_709to601 of OBSBackgroundRemovalShader +[Alias('Convert_709to601')] +[ComponentModel.DefaultBindingProperty('Convert_709to601')] +[Management.Automation.SwitchParameter] +$Convert709to601, +# Set the Convert_601to709 of OBSBackgroundRemovalShader +[Alias('Convert_601to709')] +[ComponentModel.DefaultBindingProperty('Convert_601to709')] +[Management.Automation.SwitchParameter] +$Convert601to709, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30209,28 +21664,124 @@ $UseShaderTime process { -$shaderName = 'opacity' -$ShaderNoun = 'OBSOpacityShader' +$shaderName = 'background_removal' +$ShaderNoun = 'OBSBackgroundRemovalShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Opacity shader - for OBS Shaderfilter -// Copyright 2021 by SkeltonBowTV -// https://twitter.com/skeletonbowtv -// https://twitch.tv/skeletonbowtv +// background removal effect By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Exeldro February 19, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float Opacity< - string label = "Opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.01; -> = 100.00; // 0.00 - 100.00 percent +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes = "Opacity between 10 and 20 works. Adjust `Color` from white to fix environmental changes.\r\r\nUsage:\r\n1) Disable `Auto` settings like focus, white balance, etc.\r\n2) Take a video of just the background. \r\n3) Take a frame and use it as the background image. Windows Snipping Tool (%windir%\\system32\\SnippingTool.exe). \r\r\nThis eliminates differences based upon camera/video settings."; -float4 mainImage( VertData v_in ) : TARGET +uniform texture2d target; +uniform float4 color; +uniform float opacity = 15.0; +uniform bool invert; +uniform bool Convert_709to601; +uniform bool Convert_601to709; + + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; + +float dot(float3 a,float3 b){ + return a.x*b.x+a.y*b.y+a.z*b.z; +} + +//BT.601 to BT.709 +// Correct video colorspace BT.601 [SD] to BT.709 [HD] for HD video input +// Use this shader only if BT.709 [HD] encoded video is incorrectly matrixed to full range RGB with the BT.601 [SD] colorspace. +float4 Convert601to709(float4 rgba) { - float4 color = image.Sample( textureSampler, v_in.uv ); - return float4( color.rgb, color.a * Opacity * 0.01 ); + float3 s1 = rgba.rgb; + s1 = s1.rrr * float3(0.299, -0.1495 / 0.886, 0.5) + s1.ggg * float3(0.587, -0.2935 / 0.886, -0.2935 / 0.701) + s1.bbb * float3(0.114, 0.5, -0.057 / 0.701); // RGB to Y''CbCr, BT.601 [SD] colorspace + return (s1.rrr + float3(0, -0.1674679 / 0.894, 1.8556) * s1.ggg + float3(1.5748, -0.4185031 / 0.894, 0) * s1.bbb).rgbb; // Y''CbCr to RGB output, BT.709 [HD] colorspace +} + +//BT.709 to BT.601 +float4 Convert709to601(float4 rgba) +{ + float3 s1 = rgba.rgb; + s1 = float3(dot(float3(.2126, .7152, .0722), s1), dot(float3(-.1063 / .9278, -.3576 / .9278, .5), s1), dot(float3(.5, -.3576 / .7874, -.0361 / .7874), s1)); + return float3(s1.x + 1.402*s1.z, dot(s1, float3(1, -.202008 / .587, -.419198 / .587)), s1.x + 1.772*s1.y).rgbb; +} + +VertDataOut VSDefault(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.x, v_in.pos.y, v_in.pos.z, 1.0), ViewProj); + vert_out.uv = v_in.uv; + vert_out.uv2 = v_in.uv * uv_scale + uv_offset; + return vert_out; +} + +float4 PSColorMaskRGBA(VertDataOut v_in) : TARGET +{ + float Tolerance = opacity * 0.01; + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float4 targetRGB = target.Sample(textureSampler, v_in.uv2) * color; + if (invert){ + targetRGB.rgb = 1.0 - targetRGB.rgb; + } + if (Convert_709to601) + { + rgba.rgb = Convert709to601(rgba).rgb; + targetRGB.rgb = Convert709to601(targetRGB).rgb; + } + + if (Convert_601to709) + { + rgba.rgb = Convert601to709(rgba).rgb; + targetRGB.rbg = Convert601to709(targetRGB).rgb; + } + + float4 shadowRGB = targetRGB * targetRGB; + + if ((abs(targetRGB.r - rgba.r) <= Tolerance && + abs(targetRGB.g - rgba.g) <= Tolerance && + abs(targetRGB.b - rgba.b) <= Tolerance) + || (abs(shadowRGB.r - rgba.r) <= Tolerance && + abs(shadowRGB.g - rgba.g) <= Tolerance && + abs(shadowRGB.b - rgba.b) <= Tolerance)) + { + rgba.rgba = float4(0,0,0,0); + } + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSColorMaskRGBA(v_in); + } } + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30328,18 +21879,50 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelShader { +function Get-OBSBlendOpacityShader { -[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] +[Alias('Set-OBSBlendOpacityShader','Add-OBSBlendOpacityShader')] param( -# Set the Speed of OBSPagePeelShader +# Set the Vertical of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the Rotational of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Rotational')] +[Management.Automation.SwitchParameter] +$Rotational, +# Set the Rotation_Offset of OBSBlendOpacityShader +[Alias('Rotation_Offset')] +[ComponentModel.DefaultBindingProperty('Rotation_Offset')] +[Single] +$RotationOffset, +# Set the Opacity_Start_Percent of OBSBlendOpacityShader +[Alias('Opacity_Start_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_Start_Percent')] +[Single] +$OpacityStartPercent, +# Set the Opacity_End_Percent of OBSBlendOpacityShader +[Alias('Opacity_End_Percent')] +[ComponentModel.DefaultBindingProperty('Opacity_End_Percent')] +[Single] +$OpacityEndPercent, +# Set the Spread of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Spread')] +[Single] +$Spread, +# Set the Speed of OBSBlendOpacityShader [ComponentModel.DefaultBindingProperty('Speed')] [Single] $Speed, -# Set the Position of OBSPagePeelShader -[ComponentModel.DefaultBindingProperty('Position')] -[Single] -$Position, +# Set the Apply_To_Alpha_Layer of OBSBlendOpacityShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Notes of OBSBlendOpacityShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30370,86 +21953,92 @@ $UseShaderTime process { -$shaderName = 'page-peel' -$ShaderNoun = 'OBSPagePeelShader' +$shaderName = 'blend_opacity' +$ShaderNoun = 'OBSBlendOpacityShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Page Peel, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2023 by SkeletonBow -// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License -// Contact info: -// Twitter: -// Twitch: -// YouTube: -// Soundcloud: -// -// Based on Shadertoy shader by droozle -// -// Description: -// -// -// Changelog: -// 0.01 - Initial release - -uniform float Speed< - string label = "Speed"; +// opacity blend shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 14, 2022 +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 50.0; - float step = 0.001; -> = 1.00; -uniform float Position< - string label = "Position"; + float maximum = 6.28318531; + float step = 0.01; +> = 0.0; +uniform float Opacity_Start_Percent< + string label = "Opacity Start Percent"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; - float step = 0.001; + float step = 1.0; +> = 0.0; +uniform float Opacity_End_Percent< + string label = "Opacity End Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform float Spread< + string label = "Spread"; + string widget_type = "slider"; + float minimum = 0.25; + float maximum = 10.0; + float step = 0.01; +> = 0.5; +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; > = 0.0; +uniform bool Apply_To_Alpha_Layer = true; +uniform string Notes< + string widget_type = "info"; +> = "Spread is wideness of opacity blend and is limited between .25 and 10. Edit at your own risk. Invert Start and End to Reverse effect."; -float4 mainImage( VertData v_in ) : TARGET +float4 mainImage(VertData v_in) : TARGET { - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; + float4 point_color = image.Sample(textureSampler, v_in.uv); + float luminance = 0.299*point_color.r+0.587*point_color.g+0.114*point_color.b; + float4 gray = float4(luminance,luminance,luminance, 1); - float t = Position + elapsed_time * Speed; - // Define the fold. - float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); + float2 lPos = (v_in.uv * uv_scale + uv_offset) / clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); + float dist = distance(v_in.uv , (float2(0.99, 0.99) * uv_scale + uv_offset)); - // Sample texture. - float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - col *= 0.25; // Background color (peeled off). - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - float4 back = image.Sample( textureSampler, pt ); // Back color. - back.rgb = back.rgb * 0.25 + 0.75; - - float shadow = smoothstep( 0.0, 0.2, -side ); - back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); - - // Support for transparency. - col = lerp( col, back.rgb, back.a ); - } - } - - // Output to screen - return float4(col,1.0); + if (point_color.a > 0.0 || Apply_To_Alpha_Layer == false) + { + //set opacity and direction + float opacity = (-1 * lPos.x) * 0.5; + + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + opacity = (lPos.x * cosine + lPos.y * sine) * 0.5; + } + + if (Vertical && (Rotational == false)) + { + opacity = (-1 * lPos.y) * 0.5; + } + + opacity += time; + opacity = frac(opacity); + point_color.a = lerp(Opacity_Start_Percent * 0.01, Opacity_End_Percent * 0.01, clamp(opacity, 0.0, 1.0)); + } + return point_color; } + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -30547,40 +22136,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPagePeelTransitionShader { +function Get-OBSBlinkShader { -[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] +[Alias('Set-OBSBlinkShader','Add-OBSBlinkShader')] param( -# Set the image_a of OBSPagePeelTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSPagePeelTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSPagePeelTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPagePeelTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the page_color of OBSPagePeelTransitionShader -[Alias('page_color')] -[ComponentModel.DefaultBindingProperty('page_color')] -[String] -$PageColor, -# Set the page_transparency of OBSPagePeelTransitionShader -[Alias('page_transparency')] -[ComponentModel.DefaultBindingProperty('page_transparency')] +# Set the speed of OBSBlinkShader +[ComponentModel.DefaultBindingProperty('speed')] [Single] -$PageTransparency, +$Speed, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -30611,76 +22174,24 @@ $UseShaderTime process { -$shaderName = 'page-peel-transition' -$ShaderNoun = 'OBSPagePeelTransitionShader' +$shaderName = 'blink' +$ShaderNoun = 'OBSBlinkShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; -uniform float page_transparency< - string label = "Page Transparency"; +uniform float speed< + string label = "Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; + float maximum = 100.0; + float step = 0.01; > = 0.5; float4 mainImage(VertData v_in) : TARGET { - // Normalized pixel coordinates (from 0 to 1) - float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); - float2 uv = v_in.uv; - - float t = transition_time * 12.0 + 11.0; - // Define the fold. - float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; - float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - - // Sample texture. - float3 col = float3(1.0,0.0,0.0); - - // Check on which side the pixel lies. - float2 pt = uv * aspect - origin; - float side = dot( pt, normal ); - if( side > 0.0 ) { - // Next page - col = image_b.Sample( textureSampler, uv ).rgb; - - float shadow = smoothstep( 0.0, 0.05, side ); - col = lerp( col * 0.6, col, shadow ); - } - else { - - - // Find the mirror pixel. - pt = ( uv * aspect - 2.0 * side * normal ) / aspect; - - // Check if we''re still inside the image bounds. - if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { - col = image_a.Sample( textureSampler, pt ).rgb; // Back color. - col = lerp(page_color.rgb, col, page_transparency); - - float shadow = smoothstep( 0.0, 0.2, -side ); - col = lerp( col * 0.2, col, shadow ); - }else{ - col = image_a.Sample( textureSampler, uv ).rgb; - } - } - - // Output to screen - if(convert_linear) - col = srgb_nonlinear_to_linear(col); - return float4(col,1.0); -} + float4 color = image.Sample(textureSampler, v_in.uv); + float t = elapsed_time * speed; + return float4(color.r, color.g, color.b, color.a * (1 + sin(t)) / 2); +} ' } @@ -30779,56 +22290,25 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPerlinNoiseShader { +function Get-OBSBloomShader { -[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] +[Alias('Set-OBSBloomShader','Add-OBSBloomShader')] param( -# Set the speed of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the animated of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('animated')] -[Management.Automation.SwitchParameter] -$Animated, -# Set the apply_to_channel of OBSPerlinNoiseShader -[Alias('apply_to_channel')] -[ComponentModel.DefaultBindingProperty('apply_to_channel')] -[Management.Automation.SwitchParameter] -$ApplyToChannel, -# Set the inverted of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('inverted')] -[Management.Automation.SwitchParameter] -$Inverted, -# Set the multiply of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('multiply')] -[Management.Automation.SwitchParameter] -$Multiply, -# Set the speed_horizonal of OBSPerlinNoiseShader -[Alias('speed_horizonal')] -[ComponentModel.DefaultBindingProperty('speed_horizonal')] -[Single] -$SpeedHorizonal, -# Set the speed_vertical of OBSPerlinNoiseShader -[Alias('speed_vertical')] -[ComponentModel.DefaultBindingProperty('speed_vertical')] -[Single] -$SpeedVertical, -# Set the iterations of OBSPerlinNoiseShader -[ComponentModel.DefaultBindingProperty('iterations')] +# Set the Angle_Steps of OBSBloomShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] [Int32] -$Iterations, -# Set the white_noise of OBSPerlinNoiseShader -[Alias('white_noise')] -[ComponentModel.DefaultBindingProperty('white_noise')] -[Single] -$WhiteNoise, -# Set the black_noise of OBSPerlinNoiseShader -[Alias('black_noise')] -[ComponentModel.DefaultBindingProperty('black_noise')] +$AngleSteps, +# Set the Radius_Steps of OBSBloomShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSBloomShader +[ComponentModel.DefaultBindingProperty('ampFactor')] [Single] -$BlackNoise, -# Set the notes of OBSPerlinNoiseShader +$AmpFactor, +# Set the notes of OBSBloomShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -30862,255 +22342,75 @@ $UseShaderTime process { -$shaderName = 'perlin_noise' -$ShaderNoun = 'OBSPerlinNoiseShader' +$shaderName = 'bloom' +$ShaderNoun = 'OBSBloomShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// -// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -// -// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson -// Translation and modification was made by Keijiro Takahashi. -// Conversion for OBS by Charles Fettinger. -// -// This shader is based on the webgl-noise GLSL shader. For further details -// of the original shader, please see the following description from the -// original source code. -// - // -// GLSL textureless classic 2D noise "cnoise", (white_noise) -// with an RSL-style periodic variant "pnoise" (black_noise). -// Author: Stefan Gustavson (stefan.gustavson@liu.se) -// Version: 2011-08-22 -// -// Many thanks to Ian McEwan of Ashima Arts for the -// ideas for permutation and gradient selection. -// -// Copyright (c) 2011 Stefan Gustavson. All rights reserved. -// Distributed under the MIT license. See LICENSE file. -// https://github.com/ashima/webgl-noise -//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 - float4 mod(float4 x, float4 y) -{ - return x - y * floor(x / y); -} - float4 mod289(float4 x) -{ - return x - floor(x / 289.0) * 289.0; -} - float4 permute(float4 x) -{ - return mod289(((x*34.0)+1.0)*x); -} - float4 taylorInvSqrt(float4 r) -{ - return 1.79284291400159 - r * 0.85373472095314; -} - float2 fade(float2 t) { - return t*t*t*(t*(t*6.0-15.0)+10.0); -} - // Classic Perlin noise -float cnoise(float2 P) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - // Classic Perlin noise, periodic variant -float pnoise(float2 P, float2 rep) -{ - float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); - float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); - Pi = mod(Pi, rep.xyxy); // To create noise with explicit period - Pi = mod289(Pi); // To avoid truncation effects in permutation - float4 ix = Pi.xzxz; - float4 iy = Pi.yyww; - float4 fx = Pf.xzxz; - float4 fy = Pf.yyww; - float4 i = permute(permute(ix) + iy); - float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; - float4 gy = abs(gx) - 0.5 ; - float4 tx = floor(gx + 0.5); - gx = gx - tx; - float2 g00 = float2(gx.x,gy.x); - float2 g10 = float2(gx.y,gy.y); - float2 g01 = float2(gx.z,gy.z); - float2 g11 = float2(gx.w,gy.w); - float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); - g00 *= norm.x; - g01 *= norm.y; - g10 *= norm.z; - g11 *= norm.w; - float n00 = dot(g00, float2(fx.x, fy.x)); - float n10 = dot(g10, float2(fx.y, fy.y)); - float n01 = dot(g01, float2(fx.z, fy.z)); - float n11 = dot(g11, float2(fx.w, fy.w)); - float2 fade_xy = fade(Pf.xy); - float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); - float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); - return 2.3 * n_xy; -} - //The good bits~ adapting the noise generator for the plugin and giving some control over the shader - //todo: pseudorandom number generator w/ seed -uniform float speed< - string label = "Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform bool animated; -uniform bool apply_to_channel; -uniform bool inverted; -uniform bool multiply; -uniform float speed_horizonal< - string label = "Speed horizontal"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -uniform float speed_vertical< - string label = "Speed vertical"; +// Bloom shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 15, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0; -uniform int iterations< - string label = "Iterations"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 5; // +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; int minimum = 0; int maximum = 20; int step = 1; -> = 4; -//how much c_noise do we want? white -uniform float white_noise< - string label = "White noise"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.5; -//how much p_noise do we want? black -uniform float black_noise< - string label = "Black noise"; +> = 9; // +uniform float ampFactor< + string label = "amp Factor"; string widget_type = "slider"; float minimum = 0.0; float maximum = 10.0; - float step = 0.001; -> = 0.5; + float step = 0.01; +> = 2.0; uniform string notes< string widget_type = "info"; -> = "white noise and black noise and iterations.. enjoy!"; +> = "Steps limited in range from 0 to 20. Edit bloom.shader to remove limits at your own risk."; - float2 noisePosition(float t){ - return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); -} - float4 mainImage(VertData v_in) : TARGET +float4 mainImage(VertData v_in) : TARGET { - float4 color = image.Sample(textureSampler, v_in.uv); - float t = elapsed_time * speed; - float2 dir = float2(speed_horizonal,speed_vertical); - - if(!animated){ - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = v_in.uv * scale; - float2 period = float2(scale * 2.0, scale * 2.0); - - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } - - //o += pnoise(coord, period) * w; - - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); - } - } else { - float o = 0.5; - float scale = 1.0; - float w = 0.5; - for(int i = 0; i < iterations; i++){ - float2 coord = (v_in.uv + t*dir) * scale; - float2 period = float2(scale * 2.0, scale * 2.0); + int radiusSteps = clamp(Radius_Steps, 0, 20); + int angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + float minRadius = (0.0 * uv_pixel_interval.y); + float maxRadius = (10.0 * uv_pixel_interval.y); + + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 outputPixel = c0; + float4 accumulatedColor = float4(0,0,0,0); + + int totalSteps = radiusSteps * angleSteps; + float angleDelta = (2.0 * PI) / float(angleSteps); + float radiusDelta = (maxRadius - minRadius) / float(radiusSteps); + + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + float(radiusStep) * radiusDelta; + + for (float angle=0.0; angle <(2.0*PI); angle += angleDelta) { + float2 currentCoord; + + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); - if(white_noise == 0.0 && black_noise == 0.0){ - o += pnoise(coord, period) * w; - } else { - if(white_noise != 0.0){ - o += cnoise(coord) * w * white_noise; - } - if(black_noise != 0.0){ - o += pnoise(coord, period) * w * black_noise; - } - } + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor =image.Sample(textureSampler, currentCoord); + float currentFraction = float(radiusSteps+1 - radiusStep) / float(radiusSteps + 1); + + accumulatedColor += currentFraction * currentColor / float(totalSteps); - scale *= 2.0; - w *= 0.5; - } - if(inverted){ - o = 1 - o; - } - if(apply_to_channel){ - if(multiply){ - return float4(color.r,color.g,color.b,color.a*o); - } else { - return float4(color.r,color.g,color.b,o); - } - } else { - return float4(o,o,o,1.0); } } + + outputPixel += accumulatedColor * ampFactor; + + return outputPixel; } ' @@ -31210,39 +22510,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPerspectiveShader { +function Get-OBSBorderShader { -[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] +[Alias('Set-OBSBorderShader','Add-OBSBorderShader')] param( -# Set the angle_x of OBSPerspectiveShader -[Alias('angle_x')] -[ComponentModel.DefaultBindingProperty('angle_x')] -[Single] -$AngleX, -# Set the angle_y of OBSPerspectiveShader -[Alias('angle_y')] -[ComponentModel.DefaultBindingProperty('angle_y')] -[Single] -$AngleY, -# Set the angle_z of OBSPerspectiveShader -[Alias('angle_z')] -[ComponentModel.DefaultBindingProperty('angle_z')] -[Single] -$AngleZ, -# Set the perspective of OBSPerspectiveShader -[ComponentModel.DefaultBindingProperty('perspective')] -[Single] -$Perspective, -# Set the border_color of OBSPerspectiveShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +# Set the borderColor of OBSBorderShader +[ComponentModel.DefaultBindingProperty('borderColor')] [String] $BorderColor, -# Set the show_border of OBSPerspectiveShader -[Alias('show_border')] -[ComponentModel.DefaultBindingProperty('show_border')] -[Management.Automation.SwitchParameter] -$ShowBorder, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -31273,104 +22548,24 @@ $UseShaderTime process { -$shaderName = 'perspective' -$ShaderNoun = 'OBSPerspectiveShader' +$shaderName = 'border' +$ShaderNoun = 'OBSBorderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Perspective Transform Shader for OBS -// Allows adjustable 3D perspective effects -// Usage: Add as filter in OBS via ShaderFilter plugin - -uniform float angle_x< - string label = "X Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; - -uniform float angle_y< - string label = "Y Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; - -uniform float angle_z< - string label = "Z Rotation"; - string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 1.0; -> = 0.0; - -uniform float perspective< - string label = "Perspective Strength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; - -uniform float4 border_color< - string label = "Border Color"; -> = {0.0, 0.0, 0.0, 1.0}; - -uniform bool show_border< - string label = "Show Border"; -> = true; - -float4x4 rotationMatrix(float3 angles) -{ - float radX = radians(angles.x); - float radY = radians(angles.y); - float radZ = radians(angles.z); - - float sinX = sin(radX); - float cosX = cos(radX); - float sinY = sin(radY); - float cosY = cos(radY); - float sinZ = sin(radZ); - float cosZ = cos(radZ); - - return float4x4( - cosY*cosZ, -cosY*sinZ, sinY, 0, - sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, - -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, - 0, 0, 0, 1 - ); -} +uniform float4 borderColor; float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - - // Center coordinates - float2 center = float2(0.5, 0.5); - uv -= center; - - // Apply perspective - float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); - uv *= perspectiveFactor; - - // Create rotation matrix - float3 angles = float3(angle_x, angle_y, angle_z); - float4x4 rotMat = rotationMatrix(angles); - - // Apply transformation - float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); - - // Restore center position - uv = transformed.xy + center; - - // Sample texture with border handling - if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { - return show_border ? border_color : float4(0, 0, 0, 0); + if (v_in.uv.x < 0 || v_in.uv.x > 1 || v_in.uv.y < 0 || v_in.uv.y > 1) + { + return borderColor; + } + else + { + return image.Sample(textureSampler, v_in.uv); } - - return image.Sample(textureSampler, uv); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -31468,129 +22663,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPieChartShader { +function Get-OBSBoxBlurShader { -[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] +[Alias('Set-OBSBoxBlurShader','Add-OBSBoxBlurShader')] param( -# Set the inner_radius of OBSPieChartShader -[Alias('inner_radius')] -[ComponentModel.DefaultBindingProperty('inner_radius')] -[Single] -$InnerRadius, -# Set the outer_radius of OBSPieChartShader -[Alias('outer_radius')] -[ComponentModel.DefaultBindingProperty('outer_radius')] +# Set the Strength of OBSBoxBlurShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Int32] +$Strength, +# Set the Mask_Left of OBSBoxBlurShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] [Single] -$OuterRadius, -# Set the start_angle of OBSPieChartShader -[Alias('start_angle')] -[ComponentModel.DefaultBindingProperty('start_angle')] +$MaskLeft, +# Set the Mask_Right of OBSBoxBlurShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] [Single] -$StartAngle, -# Set the total of OBSPieChartShader -[ComponentModel.DefaultBindingProperty('total')] -[Int32] -$Total, -# Set the part_1 of OBSPieChartShader -[Alias('part_1')] -[ComponentModel.DefaultBindingProperty('part_1')] -[Int32] -$Part1, -# Set the color_1 of OBSPieChartShader -[Alias('color_1')] -[ComponentModel.DefaultBindingProperty('color_1')] -[String] -$Color1, -# Set the part_2 of OBSPieChartShader -[Alias('part_2')] -[ComponentModel.DefaultBindingProperty('part_2')] -[Int32] -$Part2, -# Set the color_2 of OBSPieChartShader -[Alias('color_2')] -[ComponentModel.DefaultBindingProperty('color_2')] -[String] -$Color2, -# Set the part_3 of OBSPieChartShader -[Alias('part_3')] -[ComponentModel.DefaultBindingProperty('part_3')] -[Int32] -$Part3, -# Set the color_3 of OBSPieChartShader -[Alias('color_3')] -[ComponentModel.DefaultBindingProperty('color_3')] -[String] -$Color3, -# Set the part_4 of OBSPieChartShader -[Alias('part_4')] -[ComponentModel.DefaultBindingProperty('part_4')] -[Int32] -$Part4, -# Set the color_4 of OBSPieChartShader -[Alias('color_4')] -[ComponentModel.DefaultBindingProperty('color_4')] -[String] -$Color4, -# Set the part_5 of OBSPieChartShader -[Alias('part_5')] -[ComponentModel.DefaultBindingProperty('part_5')] -[Int32] -$Part5, -# Set the color_5 of OBSPieChartShader -[Alias('color_5')] -[ComponentModel.DefaultBindingProperty('color_5')] -[String] -$Color5, -# Set the part_6 of OBSPieChartShader -[Alias('part_6')] -[ComponentModel.DefaultBindingProperty('part_6')] -[Int32] -$Part6, -# Set the color_6 of OBSPieChartShader -[Alias('color_6')] -[ComponentModel.DefaultBindingProperty('color_6')] -[String] -$Color6, -# Set the part_7 of OBSPieChartShader -[Alias('part_7')] -[ComponentModel.DefaultBindingProperty('part_7')] -[Int32] -$Part7, -# Set the color_7 of OBSPieChartShader -[Alias('color_7')] -[ComponentModel.DefaultBindingProperty('color_7')] -[String] -$Color7, -# Set the part_8 of OBSPieChartShader -[Alias('part_8')] -[ComponentModel.DefaultBindingProperty('part_8')] -[Int32] -$Part8, -# Set the color_8 of OBSPieChartShader -[Alias('color_8')] -[ComponentModel.DefaultBindingProperty('color_8')] -[String] -$Color8, -# Set the part_9 of OBSPieChartShader -[Alias('part_9')] -[ComponentModel.DefaultBindingProperty('part_9')] -[Int32] -$Part9, -# Set the color_9 of OBSPieChartShader -[Alias('color_9')] -[ComponentModel.DefaultBindingProperty('color_9')] -[String] -$Color9, -# Set the part_10 of OBSPieChartShader -[Alias('part_10')] -[ComponentModel.DefaultBindingProperty('part_10')] -[Int32] -$Part10, -# Set the color_10 of OBSPieChartShader -[Alias('color_10')] -[ComponentModel.DefaultBindingProperty('color_10')] -[String] -$Color10, +$MaskRight, +# Set the Mask_Top of OBSBoxBlurShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSBoxBlurShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -31621,159 +22721,89 @@ $UseShaderTime process { -$shaderName = 'pie-chart' -$ShaderNoun = 'OBSPieChartShader' +$shaderName = 'box-blur' +$ShaderNoun = 'OBSBoxBlurShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float inner_radius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 32.0; -uniform float outer_radius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 50.0; -uniform float start_angle< - string label = "Start angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 90.0; - -uniform int total< - string label = "Total"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 100; -uniform int part_1< - string label = "Part 1"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 50; -uniform float4 color_1 = {0.0,0.26,0.62,1.0}; -uniform int part_2< - string label = "Part 2"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 25; -uniform float4 color_2 = {0.24,0.40,0.68,1.0}; -uniform int part_3< - string label = "Part 3"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 10; -uniform float4 color_3 = {0.38,0.56,0.75,1.0}; -uniform int part_4< - string label = "Part 4"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 5; -uniform float4 color_4 = {0.52,0.72,0.81,1.0}; -uniform int part_5< - string label = "Part 5"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 3; -uniform float4 color_5 = {0.69,0.87,0.86,1.0}; -uniform int part_6< - string label = "Part 6"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 2; -uniform float4 color_6 = {1.0,0.79,0.73,1.0}; -uniform int part_7< - string label = "Part 7"; +uniform int Strength< + string label = "Strength (1)"; string widget_type = "slider"; int minimum = 0; - int maximum = 1000; + int maximum = 25; int step = 1; > = 1; -uniform float4 color_7 = {0.99,0.57,0.57,1.0}; -uniform int part_8< - string label = "Part 8"; +uniform float Mask_Left< + string label = "Mask left (1.0)"; string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_8 = {0.91,0.36,0.44,1.0}; -uniform int part_9< - string label = "Part 9"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 1; -uniform float4 color_9 = {0.77,0.16,0.32,1.0}; -uniform int part_10< - string label = "Part 10"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; string widget_type = "slider"; - int minimum = 0; - int maximum = 1000; - int step = 1; -> = 0; -uniform float4 color_10 = {0.58,0.0,0.23,1.0}; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; float4 mainImage(VertData v_in) : TARGET { - const float pi = 3.14159265358979323846; -#ifdef OPENGL - float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); - float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); -#else - float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; - float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; -#endif - float2 center = float2(0.5, 0.5); - float2 factor; - if(uv_size.x < uv_size.y){ - factor = float2(1.0, uv_size.y/uv_size.x); - }else{ - factor = float2(uv_size.x/uv_size.y, 1.0); - } - center = center * factor; - float d = distance(center, v_in.uv * factor); - if(d > outer_radius/100.0 || d < inner_radius/100.0){ + if(Strength <= 0) return image.Sample(textureSampler, v_in.uv); - } - float2 toCenter = center - v_in.uv*factor; - float angle = atan2(toCenter.y ,toCenter.x); - angle = angle - (start_angle / 180.0 * pi); - if(angle < 0.0) - angle = pi + pi + angle; - if(angle < 0.0) - angle = pi + pi + angle; - angle = angle / (pi + pi); - float t = 0.0; - for(int i = 0; i < 10; i+=1) { - float part = parts[i]/total; - if(angle > t && angle <= t+part){ - return colors[i]; + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + float transparent = 0.0; + int count = 1; + float samples = 0.0; + float4 c = float4(0.0, 0.0, 0.0, 0.0); + float Steps = float(Strength); + + [loop] for (int i = -Strength; i <= Strength; i++) { + [loop] for (int k = -Strength; k <= Strength; k++) { + float4 sc = image.Sample(textureSampler, v_in.uv+float2(float(i), float(k))/uv_size*Steps); + transparent += sc.a; + count++; + c += sc * sc.a; + samples += sc.a; } - t = t + part; } - return image.Sample(textureSampler, v_in.uv); + if(samples > 0.0) + c /= samples; + + c.a = transparent / float(count); + return c; } ' } @@ -31872,21 +22902,33 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationShader { +function Get-OBSBulgePinchShader { -[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] +[Alias('Set-OBSBulgePinchShader','Add-OBSBulgePinchShader')] param( -# Set the Target_Width of OBSPixelationShader -[Alias('Target_Width')] -[ComponentModel.DefaultBindingProperty('Target_Width')] +# Set the radius of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('radius')] [Single] -$TargetWidth, -# Set the Target_Height of OBSPixelationShader -[Alias('Target_Height')] -[ComponentModel.DefaultBindingProperty('Target_Height')] +$Radius, +# Set the magnitude of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('magnitude')] [Single] -$TargetHeight, -# Set the notes of OBSPixelationShader +$Magnitude, +# Set the center_x of OBSBulgePinchShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSBulgePinchShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the animate of OBSBulgePinchShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the notes of OBSBulgePinchShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -31920,55 +22962,78 @@ $UseShaderTime process { -$shaderName = 'pixelation' -$ShaderNoun = 'OBSPixelationShader' +$shaderName = 'BulgePinch' +$ShaderNoun = 'OBSBulgePinchShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// with help from SkeltonBowTV -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 15, 2022 -uniform float Target_Width< - string label = "Target Width"; +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 320.0; -uniform float Target_Height< - string label = "Target Height"; + float maximum = 2.0; + float step = 0.01; +> = 0.0; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = -1.3333; + float maximum = 1.3333; + float step = 0.01; +> = 0.0; +uniform float center_x< + string label = "Center x"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 2000.0; - float step = 0.1; -> = 180.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.01; +> = 0.25; +uniform bool animate = false; + uniform string notes< string widget_type = "info"; -> = "adjust width and height to your screen dimension"; +> = "Distorts the screen, expanding or drawing in pixels around a point." -float4 mainImage(VertData v_in) : TARGET + +float4 mainImage(VertData v_in) : TARGET { - float targetWidth = Target_Width; - if(targetWidth < 2.0) - targetWidth = 2.0; - float targetHeight = Target_Height; - if(targetHeight < 2.0) - targetHeight = 2.0; - float2 tex1; - int pixelSizeX = int(uv_size.x / targetWidth); - int pixelSizeY = int(uv_size.y / targetHeight); + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; + v_out.uv = 1. * v_in.uv - center; - int pixelX = int(v_in.uv.x * uv_size.x); - int pixelY = int(v_in.uv.y * uv_size.y); + center.x /= ar; + v_out.uv.x /= ar; - tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; - tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float anim_mag = (animate ? magnitude * sin(radians(elapsed_time * 20)) : magnitude); + float percent = dist/radius; + if(anim_mag > 0) + v_out.uv = (v_out.uv - center) * lerp(1.0, smoothstep(0.0, radius/dist, percent), anim_mag * 0.75); + else + v_out.uv = (v_out.uv-center) * lerp(1.0, pow(percent, 1.0 + anim_mag * 0.75) * radius/dist, 1.0 - percent); - float4 c1 = image.Sample(textureSampler, tex1 ); + v_out.uv += (2 * center); + v_out.uv.x *= ar; - return c1; + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32066,43 +23131,70 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPixelationTransitionShader { +function Get-OBSBurnShader { -[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] +[Alias('Set-OBSBurnShader','Add-OBSBurnShader')] param( -# Set the transition_time of OBSPixelationTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSPixelationTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] -[Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the power of OBSPixelationTransitionShader -[ComponentModel.DefaultBindingProperty('power')] +# Set the Burn_Gradient of OBSBurnShader +[Alias('Burn_Gradient')] +[ComponentModel.DefaultBindingProperty('Burn_Gradient')] +[String] +$BurnGradient, +# Set the Speed of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Speed')] [Single] -$Power, -# Set the center_x of OBSPixelationTransitionShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] +$Speed, +# Set the Gradient_Adjust of OBSBurnShader +[Alias('Gradient_Adjust')] +[ComponentModel.DefaultBindingProperty('Gradient_Adjust')] [Single] -$CenterX, -# Set the center_y of OBSPixelationTransitionShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] +$GradientAdjust, +# Set the Dissolve_Value of OBSBurnShader +[Alias('Dissolve_Value')] +[ComponentModel.DefaultBindingProperty('Dissolve_Value')] [Single] -$CenterY, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, +$DissolveValue, +# Set the Animated of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Animated')] +[Management.Automation.SwitchParameter] +$Animated, +# Set the Apply_to_Channel of OBSBurnShader +[Alias('Apply_to_Channel')] +[ComponentModel.DefaultBindingProperty('Apply_to_Channel')] +[Management.Automation.SwitchParameter] +$ApplyToChannel, +# Set the Apply_Smoke of OBSBurnShader +[Alias('Apply_Smoke')] +[ComponentModel.DefaultBindingProperty('Apply_Smoke')] +[Management.Automation.SwitchParameter] +$ApplySmoke, +# Set the Smoke_Horizonal_Speed of OBSBurnShader +[Alias('Smoke_Horizonal_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Horizonal_Speed')] +[Single] +$SmokeHorizonalSpeed, +# Set the Smoke_Vertical_Speed of OBSBurnShader +[Alias('Smoke_Vertical_Speed')] +[ComponentModel.DefaultBindingProperty('Smoke_Vertical_Speed')] +[Single] +$SmokeVerticalSpeed, +# Set the Iterations of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Iterations')] +[Int32] +$Iterations, +# Set the Notes of OBSBurnShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, # The inline value of the shader. This will normally be provided as a default parameter, based off of the name. [Alias('ShaderContent')] [String] @@ -32124,63 +23216,169 @@ $UseShaderTime process { -$shaderName = 'pixelation-transition' -$ShaderNoun = 'OBSPixelationTransitionShader' +$shaderName = 'burn' +$ShaderNoun = 'OBSBurnShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float transition_time< - string label = "Transittion Time"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform bool convert_linear = true; -uniform float power< - string label = "Power"; - string widget_type = "slider"; - float minimum = 0.5; - float maximum = 8.0; - float step = 0.01; -> = 3.0; -uniform float center_x< - string label = "X"; - string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Y"; - string widget_type = "slider"; - string group = "Center"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; +//Burn shader by Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//for use with obs-shaderfilter 1.0 +//Converted to OpenGL by Exeldro February 17, 2022 +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} +float4 mod289(float4 x) +{ + return x - floor(x / 289.0) * 289.0; +} +float4 permute(float4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} +float4 taylorInvSqrt(float4 r) +{ + return 1.79284291400159 - r * 0.85373472095314; +} +float2 fade(float2 t) { + return t * t* t* (t * (t * 6.0 - 15.0) + 10.0); +} + +float dot(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; +} + +// Classic Perlin noise +float cnoise(float2 P) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} +// Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0; + float4 gy = abs(gx) - 0.5; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x, gy.x); + float2 g10 = float2(gx.y, gy.y); + float2 g01 = float2(gx.z, gy.z); + float2 g11 = float2(gx.w, gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + +uniform texture2d Burn_Gradient = "burngradient.png"; +uniform float Speed = 0.33; +uniform float Gradient_Adjust = 0.85; +uniform float Dissolve_Value = 1.43; +uniform bool Animated; +uniform bool Apply_to_Channel; +uniform bool Apply_Smoke = true; +uniform float Smoke_Horizonal_Speed = 0.3; +uniform float Smoke_Vertical_Speed = 0.17; +uniform int Iterations = 4; +uniform string Notes< + string widget_type = "info"; +> = "Animate refers to the burn effect. Speed is general and is reversed with negative numbers. Gradient Adjust is the width of the burn gradient. Use the burngradient.png. Dissolve Value is important. Apply Smoke adds the scrolling smoke."; float4 mainImage(VertData v_in) : TARGET { - //1..0..1 - float scale = abs(transition_time - 0.5) * 2.0; - scale = pow(scale, power); + float4 c = image.Sample(textureSampler, v_in.uv); + float4 smoke = float4(1.0,1.0,1.0,1.0); + float4 result = smoke; + float t = elapsed_time * Speed; + float cycle = 1 - max((sin(t) * 2) - 1, 0); //create a negative cycle time as a delay + float2 dir = float2(Smoke_Horizonal_Speed, Smoke_Vertical_Speed); + //float largestDistance = sqrt(pow(uv_size.x, 2) + pow(uv_size.y, 2)); - float2 uv = v_in.uv; - uv -= float2(center_x, center_y); - uv *= uv_size; - uv *= scale; - uv = floor(uv); - uv /= scale; - uv /= uv_size; - uv += float2(center_x, center_y); - uv = clamp(uv, 1.0/uv_size, 1.0); - float4 rgba = image.Sample(textureSampler, uv); - if(convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; -} + float perlin = 0.5; + float smoke_perlin = 0; + float scale = 1; + float w = 0.5; + + for (int i = 0; i < Iterations; i++) { + //float2 coord = v_in.uv * scale;// (v_in.uv + t * dir)* scale; + float2 coord = (v_in.uv + t * (dir * .1)) * scale; + float2 period = scale * dir; + perlin += pnoise(coord, period) * w; + if (Apply_Smoke) + smoke_perlin += cnoise((v_in.uv + t * dir) * scale) * w * .5; + + scale *= 2.0; + w *= 0.5; + } + + //float toPoint = abs(length(v_in.uv - (v_in.uv * .5)) / ((1.0001 - t) * largestDistance)); + if (!Animated) + cycle = 1; + float d = clamp(((Dissolve_Value * cycle + perlin) ) - 1.0, -.01, 0.99); + float overOne = saturate(d * Gradient_Adjust); + float4 burn = Burn_Gradient.Sample(textureSampler, float2(overOne, 0.5)); + + if (Apply_to_Channel) { + result = c * burn; + } + else { + result = float4(perlin, perlin, perlin, 1.0) * burn; + } + + if (smoke_perlin > 0) { + smoke *= smoke_perlin; + if (result.a <= 0.04) + result = float4(smoke.rgb, smoke_perlin); + result += smoke; + } + return result; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32278,41 +23476,67 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPolarShader { +function Get-OBSCartoonShader { -[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] +[Alias('Set-OBSCartoonShader','Add-OBSCartoonShader')] param( -# Set the center_x of OBSPolarShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSPolarShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] +# Set the ViewProj of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSCartoonShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$CenterY, -# Set the point_y of OBSPolarShader -[Alias('point_y')] -[ComponentModel.DefaultBindingProperty('point_y')] +$ElapsedTime, +# Set the uv_offset of OBSCartoonShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSCartoonShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSCartoonShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSCartoonShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$PointY, -# Set the flip of OBSPolarShader -[ComponentModel.DefaultBindingProperty('flip')] +$RandF, +# Set the uv_size of OBSCartoonShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the notes of OBSCartoonShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the hue_steps of OBSCartoonShader +[Alias('hue_steps')] +[ComponentModel.DefaultBindingProperty('hue_steps')] +[Int32] +$HueSteps, +# Set the value_steps of OBSCartoonShader +[Alias('value_steps')] +[ComponentModel.DefaultBindingProperty('value_steps')] +[Int32] +$ValueSteps, +# Set the Apply_To_Alpha_Layer of OBSCartoonShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] [Management.Automation.SwitchParameter] -$Flip, -# Set the rotate of OBSPolarShader -[ComponentModel.DefaultBindingProperty('rotate')] -[Single] -$Rotate, -# Set the repeat of OBSPolarShader -[ComponentModel.DefaultBindingProperty('repeat')] -[Single] -$Repeat, -# Set the scale of OBSPolarShader -[ComponentModel.DefaultBindingProperty('scale')] -[Single] -$Scale, +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32343,84 +23567,106 @@ $UseShaderTime process { -$shaderName = 'polar' -$ShaderNoun = 'OBSPolarShader' +$shaderName = 'cartoon' +$ShaderNoun = 'OBSCartoonShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -#define PI 3.14159265359 -#define PI_2 6.2831 -#define mod(x,y) (x - y * floor(x / y)) +//Darklink''s shader modified to by Charles ''Surn'' Fettinger for use with obs-shaderfilter 3/2019 +uniform float4x4 ViewProj; +uniform texture2d image; -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform string notes = "5/2 seems reasonable"; -uniform float point_y< - string label = "Point y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +uniform int hue_steps = 5; +uniform int value_steps = 2; +uniform bool Apply_To_Alpha_Layer = true; -uniform bool flip; +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; -uniform float rotate< - string label = "Rotate"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -uniform float repeat< - string label = "Repeat"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 1.0; +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -uniform float scale< - string label = "Scale"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; +VertDataOut VSDefault(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} -float4 mainImage(VertData v_in) : TARGET +float3 rgb2hsv(float3 c) { - float2 uv = v_in.uv; - uv.x -= center_x ; - uv.y -= center_y ; - uv.x = uv.x * ( uv_size.x / uv_size.y); - float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; - if(repeat < 1.0){ - pixel_angle = mod(pixel_angle+rotate,1.0); - if(pixel_angle > repeat) - return float4(0,0,0,0); - pixel_angle = mod(pixel_angle/repeat,1.0); - } else { - pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} + +float fit(float v, int factor) +{ + return round(v * factor) / factor; +} + +float hue_wrap(float h) +{ + return fmod(h + 1, 2) - 1; + if(h > 1) + return h - 2; + if(h < -1) + return h + 2; + return h; +} + +float4 PassThrough(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float3 hsv = rgb2hsv(rgba.rgb); + hsv = float3(fit(hsv.x, hue_steps), hsv.y, fit(hsv.z, value_steps)); + //hsv = float3(hue_wrap(hsv.x + 0.5), 1, hsv.z); + rgba = float4(hsv2rgb(hsv), rgba.a); + //return float4(fit(rgba.r), fit(rgba.g), fit(rgba.b), rgba.a); + } + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PassThrough(v_in); } - float pixel_distance = length(uv)/ scale - point_y; - float2 uv2 = float2(pixel_angle , pixel_distance); - if(flip) - uv2 = float2(1.0,1.0) - uv2; - return image.Sample(textureSampler,uv2); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32518,62 +23764,28 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSPulseShader { +function Get-OBSCellShadedShader { -[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] +[Alias('Set-OBSCellShadedShader','Add-OBSCellShadedShader')] param( -# Set the ViewProj of OBSPulseShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSPulseShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSPulseShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSPulseShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSPulseShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSPulseShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSPulseShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSPulseShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the speed of OBSPulseShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSPulseShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSPulseShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +# Set the Angle_Steps of OBSCellShadedShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] +[Int32] +$AngleSteps, +# Set the Radius_Steps of OBSCellShadedShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('ampFactor')] [Single] -$MaxGrowthPixels, +$AmpFactor, +# Set the notes of OBSCellShadedShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32604,84 +23816,76 @@ $UseShaderTime process { -$shaderName = 'pulse' -$ShaderNoun = 'OBSPulseShader' +$shaderName = 'cell_shaded' +$ShaderNoun = 'OBSCellShadedShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; - -uniform float speed< - string label = "Speed"; +// Cell Shaded shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 5; +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 0.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 9; +uniform float ampFactor< + string label = "amp Factor"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 200.0; - -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; + float maximum = 100.0; + float step = 0.01; +> = 2.0; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit cell_shaded.shader to remove limits at your own risk."; -VertData mainTransform(VertData v_in) +float4 mainImage(VertData v_in) : TARGET { - VertData vert_out; + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (3 * uv_pixel_interval.y); + float maxRadius = (24 * uv_pixel_interval.y); - float3 pos = v_in.pos.xyz; - float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); - float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; - float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); - float t = (1 + sin(elapsed_time * speed)) / 2; - float3 current_pos = min_pos * (1 - t) + max_pos * t; + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); - vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; -} + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} + for (float angle=0; angle <(2*PI); angle += angleDelta) { + float2 currentCoord; -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); + + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = (radiusSteps + 1 - radiusStep) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps; + + } } + accumulatedColor *= ampFactor; + + return c0 - accumulatedColor; // Cell shaded style } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -32779,50 +23983,48 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSQuadrilateralCropShader { +function Get-OBSChromaticAberrationShader { -[Alias('Set-OBSQuadrilateralCropShader','Add-OBSQuadrilateralCropShader')] +[Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] param( -# Set the Top_Left_X of OBSQuadrilateralCropShader -[Alias('Top_Left_X')] -[ComponentModel.DefaultBindingProperty('Top_Left_X')] -[Single] -$TopLeftX, -# Set the Top_Left_Y of OBSQuadrilateralCropShader -[Alias('Top_Left_Y')] -[ComponentModel.DefaultBindingProperty('Top_Left_Y')] -[Single] -$TopLeftY, -# Set the Top_Right_X of OBSQuadrilateralCropShader -[Alias('Top_Right_X')] -[ComponentModel.DefaultBindingProperty('Top_Right_X')] -[Single] -$TopRightX, -# Set the Top_Right_Y of OBSQuadrilateralCropShader -[Alias('Top_Right_Y')] -[ComponentModel.DefaultBindingProperty('Top_Right_Y')] -[Single] -$TopRightY, -# Set the Bottom_Left_X of OBSQuadrilateralCropShader -[Alias('Bottom_Left_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] -[Single] -$BottomLeftX, -# Set the Bottom_Left_Y of OBSQuadrilateralCropShader -[Alias('Bottom_Left_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] -[Single] -$BottomLeftY, -# Set the Bottom_Right_X of OBSQuadrilateralCropShader -[Alias('Bottom_Right_X')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +# Set the power of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('power')] [Single] -$BottomRightX, -# Set the Bottom_Right_Y of OBSQuadrilateralCropShader -[Alias('Bottom_Right_Y')] -[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +$Power, +# Set the gamma of OBSChromaticAberrationShader +[ComponentModel.DefaultBindingProperty('gamma')] [Single] -$BottomRightY, +$Gamma, +# Set the num_iter of OBSChromaticAberrationShader +[Alias('num_iter')] +[ComponentModel.DefaultBindingProperty('num_iter')] +[Int32] +$NumIter, +# Set the distort_radial of OBSChromaticAberrationShader +[Alias('distort_radial')] +[ComponentModel.DefaultBindingProperty('distort_radial')] +[Management.Automation.SwitchParameter] +$DistortRadial, +# Set the distort_barrel of OBSChromaticAberrationShader +[Alias('distort_barrel')] +[ComponentModel.DefaultBindingProperty('distort_barrel')] +[Management.Automation.SwitchParameter] +$DistortBarrel, +# Set the offset_spectrum_ycgco of OBSChromaticAberrationShader +[Alias('offset_spectrum_ycgco')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_ycgco')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYcgco, +# Set the offset_spectrum_yuv of OBSChromaticAberrationShader +[Alias('offset_spectrum_yuv')] +[ComponentModel.DefaultBindingProperty('offset_spectrum_yuv')] +[Management.Automation.SwitchParameter] +$OffsetSpectrumYuv, +# Set the use_random of OBSChromaticAberrationShader +[Alias('use_random')] +[ComponentModel.DefaultBindingProperty('use_random')] +[Management.Automation.SwitchParameter] +$UseRandom, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -32853,84 +24055,215 @@ $UseShaderTime process { -$shaderName = 'quadrilateral_crop' -$ShaderNoun = 'OBSQuadrilateralCropShader' +$shaderName = 'chromatic-aberration' +$ShaderNoun = 'OBSChromaticAberrationShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Quadrilateral Crop shader (inverse of a corner pin): transform a 4 points polygon to the corners of the source. -// Useful to revert perspective. - -uniform float Top_Left_X< - string label = "Top Left X"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 0; -uniform float Top_Left_Y< - string label = "Top Left Y"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 0; -uniform float Top_Right_X< - string label = "Top Right X"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 100.; -uniform float Top_Right_Y< - string label = "Top Right Y"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 0; -uniform float Bottom_Left_X< - string label = "Bottom Left X"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 0; -uniform float Bottom_Left_Y< - string label = "Bottom Left Y"; +//based on https://www.shadertoy.com/view/XssGz8 +//Converted to OpenGL by Exeldro February 14, 2022 + black background removed February 23, 2022 +uniform float power< + string label = "Power"; string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; + float minimum = 0.0; + float maximum = 2.0; float step = 0.01; -> = 100.; -uniform float Bottom_Right_X< - string label = "Bottom Right X"; +> = 0.01; +uniform float gamma< + string label = "Gamma"; string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; + float minimum = 0.01; + float maximum = 3.0; float step = 0.01; -> = 100.; -uniform float Bottom_Right_Y< - string label = "Bottom Right Y"; +> = 2.2; +uniform int num_iter< + string label = "Number iterations"; string widget_type = "slider"; - float minimum = 0; - float maximum = 100.0; - float step = 0.01; -> = 100.; + int minimum = 3; + int maximum = 25; + int step = 1; +> = 7; +uniform bool distort_radial = false; +uniform bool distort_barrel = false; +uniform bool offset_spectrum_ycgco = false; +uniform bool offset_spectrum_yuv = false; +uniform bool use_random = true; -float4 mainImage( VertData v_in ) : TARGET { - - float2 tl = float2(Top_Left_X, Top_Left_Y) * .01; - float2 tr = float2(Top_Right_X, Top_Right_Y) * .01; - float2 bl = float2(Bottom_Left_X, Bottom_Left_Y) * .01; - float2 br = float2(Bottom_Right_X, Bottom_Right_Y) * .01; - - float2 t = lerp(tl, tr, v_in.uv[0]); - float2 b = lerp(bl, br, v_in.uv[0]); - float2 uv = lerp(t, b, v_in.uv[1]); +float2 remap( float2 t, float2 a, float2 b ) { + return clamp( (t - a) / (b - a), 0.0, 1.0 ); +} - return image.Sample(textureSampler, uv); +float3 spectrum_offset_rgb( float t ) +{ + float t0 = 3.0 * t - 1.5; + float3 ret = clamp( float3( -t0, 1.0-abs(t0), t0), 0.0, 1.0); + return ret; +} + + +float3 lin2srgb( float3 c ) +{ + return pow( c, float3(gamma, gamma, gamma) ); +} +float3 srgb2lin( float3 c ) +{ + return pow( c, float3(1.0/gamma, 1.0/gamma, 1.0/gamma)); +} + +float3 yCgCo2rgb(float3 ycc) +{ + float R = ycc.x - ycc.y + ycc.z; + float G = ycc.x + ycc.y; + float B = ycc.x - ycc.y - ycc.z; + return float3(R,G,B); +} + +float3 spectrum_offset_ycgco( float t ) +{ + //float3 ygo = float3( 1.0, 1.5*t, 0.0 ); //green-pink + //float3 ygo = float3( 1.0, -1.5*t, 0.0 ); //green-purple + float3 ygo = float3( 1.0, 0.0, -1.25*t ); //cyan-orange + //float3 ygo = float3( 1.0, 0.0, 1.5*t ); //brownyello-blue + return yCgCo2rgb( ygo ); +} + +float3 yuv2rgb( float3 yuv ) +{ + float3 rgb; + rgb.r = yuv.x + yuv.z * 1.13983; + rgb.g = yuv.x + dot( float2(-0.39465, -0.58060), yuv.yz ); + rgb.b = yuv.x + yuv.y * 2.03211; + return rgb; +} + +float2 radialdistort(float2 coord, float2 amt) +{ + float2 cc = coord - 0.5; + return coord + 2.0 * cc * amt; +} + +float2 barrelDistortion( float2 p, float2 amt ) +{ + p = 2.0 * p - 1.0; + + /* + const float maxBarrelPower = 5.0; + //note: http://glsl.heroku.com/e#3290.7 , copied from Little Grasshopper + float theta = atan(p.y, p.x); + float2 radius = float2( length(p) ); + radius = pow(radius, 1.0 + maxBarrelPower * amt); + p.x = radius.x * cos(theta); + p.y = radius.y * sin(theta); + + /*/ + // much faster version + //const float maxBarrelPower = 5.0; + //float radius = length(p); + float maxBarrelPower = sqrt(5.0); + float radius = dot(p,p); //faster but doesn''t match above accurately + p *= pow(float2(radius, radius), maxBarrelPower * amt); + /* */ + + return p * 0.5 + 0.5; +} + +float2 brownConradyDistortion(float2 uv, float dist) +{ + uv = uv * 2.0 - 1.0; + // positive values of K1 give barrel distortion, negative give pincushion + float barrelDistortion1 = 0.1 * dist; // K1 in text books + float barrelDistortion2 = -0.025 * dist; // K2 in text books + + float r2 = dot(uv,uv); + uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2; + //uv *= 1.0 + barrelDistortion1 * r2; + + // tangential distortion (due to off center lens elements) + // is not modeled in this function, but if it was, the terms would go here + return uv * 0.5 + 0.5; +} + +float2 distort( float2 uv, float t, float2 min_distort, float2 max_distort ) +{ + float2 dist = float2(min_distort.x * (1.0-t) +max_distort.x * t, min_distort.y * (1.0-t) +max_distort.y * t); + //float2 dist = mix( min_distort, max_distort, t ); + if (distort_radial) + return radialdistort( uv, 2.0 * dist ); + + if(distort_barrel) + return barrelDistortion( uv, 1.75 * dist ); //distortion at center + return brownConradyDistortion( uv, 75.0 * dist.x ); +} + +// ==== + +float3 spectrum_offset_yuv( float t ) +{ + //float3 yuv = float3( 1.0, 3.0*t, 0.0 ); //purple-green + //float3 yuv = float3( 1.0, 0.0, 2.0*t ); //purple-green + float3 yuv = float3( 1.0, 0.0, -1.0*t ); //cyan-orange + //float3 yuv = float3( 1.0, -0.75*t, 0.0 ); //brownyello-blue + return yuv2rgb( yuv ); +} + +float3 spectrum_offset( float t ) +{ + if(offset_spectrum_ycgco) + return spectrum_offset_ycgco( t ); + if(offset_spectrum_yuv) + return spectrum_offset_yuv( t ); + return spectrum_offset_rgb( t ); + //return srgb2lin( spectrum_offset_rgb( t ) ); + //return lin2srgb( spectrum_offset_rgb( t ) ); } +float4 mainImage(VertData v_in) : TARGET +{ + float2 max_distort = float2(power, power); + float2 min_distort = 0.5 * max_distort; + + float2 oversiz = distort(float2(1.0, 1.0), 1.0, min_distort, max_distort); + + float2 uv = remap( v_in.uv, 1.0-oversiz, oversiz ); + + //debug oversiz + //float2 distuv = distort( uv, 1.0, max_distort ); + //if ( abs(distuv.x-0.5)>0.5 || abs(distuv.y-0.5)>0.5) + //{ + // fragColor = float4( 1.0, 0.0, 0.0, 1.0 ); return; + //} + + + float stepsiz = 1.0 / (float(num_iter)-1.0); + float rnd = 0.0; + if(use_random) + rnd = rand_f; + + float t = rnd * stepsiz; + + float3 sumcol = float3(0.0, 0.0, 0.0); + float3 sumw = float3(0.0, 0.0, 0.0); + float colA = 0.0; + + for ( int i=0; i = 0.8; // -uniform float Luminosity< - string label = "Luminosity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; // -uniform float Spread< - string label = "Spread"; - string widget_type = "slider"; - float minimum = 0.5; - float maximum = 10.0; - float step = 0.01; -> = 3.8; // -uniform float Speed< - string label = "Speed"; +//based on https://www.shadertoy.com/view/WsdyRN + +//Higher values = less distortion +uniform float distortion< + string label = "Distortion"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; + float minimum = 5.0; + float maximum = 1000.0; float step = 0.01; -> = 2.4; // -uniform float Alpha_Percentage< - string label = "Rotation Offset"; +> = 75.; +//Higher values = tighter distortion +uniform float amplitude< + string label = "Amplitude"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; - float step = 0.1; -> = 100.0; // -uniform bool Vertical; -uniform bool Rotational; -uniform float Rotation_Offset< - string label = "Rotation Offset"; + float step = 0.01; +> = 10.; +//Higher values = more color distortion +uniform float chroma< + string label = "Chroma"; string widget_type = "slider"; float minimum = 0.0; float maximum = 6.28318531; - float step = 0.001; -> = 0.0; // -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform string Notes< - string widget_type = "info"; -> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; - -float hueToRGB(float v1, float v2, float vH) { - vH = frac(vH); - if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); - if ((2.0 * vH) < 1.0) return (v2); - if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); - return clamp(v1, 0.0, 1.0); -} + float step = 0.01; +> = .5; -float4 HSLtoRGB(float4 hsl) { - float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); - float v1 = 0.0; - float v2 = 0.0; - - if (hsl.y == 0) { - rgb.xyz = hsl.zzz; - } - else { - - if (hsl.z < 0.5) { - v2 = hsl.z * (1 + hsl.y); - } - else { - v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); - } - - v1 = 2.0 * hsl.z - v2; - - rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); - rgb.y = hueToRGB(v1, v2, hsl.x); - rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); - - } - - return rgb; +float2 zoomUv(float2 uv, float zoom) { + float2 uv1 = uv; + uv1 += .5; + uv1 += zoom/2.-1.; + uv1 /= zoom; + return uv1; } float4 mainImage(VertData v_in) : TARGET { - float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); - float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); - - //set colors and direction - float hue = (-1 * lPos.x) / 2.0; - - if (Rotational && (Vertical == false)) - { - float timeWithOffset = time + Rotation_Offset; - float sine = sin(timeWithOffset); - float cosine = cos(timeWithOffset); - hue = (lPos.x * cosine + lPos.y * sine) * 0.5; - } - - if (Vertical && (Rotational == false)) - { - hue = (-1 * lPos.y) * 0.5; - } + float2 uvt = v_in.uv; + + float2 uvtR = uvt; + float2 uvtG = uvt; + float2 uvtB = uvt; + + //Uncomment the following line to get varying chroma distortion + //chroma = sin(elapsed_time)/2.+.5; + + uvtR += float2(sin(uvt.y*amplitude+elapsed_time)/distortion, cos(uvt.x*amplitude+elapsed_time)/distortion); + uvtG += float2(sin(uvt.y*amplitude+elapsed_time+chroma)/distortion, cos(uvt.x*amplitude+elapsed_time+chroma)/distortion); + uvtB += float2(sin(uvt.y*amplitude+elapsed_time+(chroma*2.))/distortion, cos(uvt.x*amplitude+elapsed_time+(chroma*2.))/distortion); + + float2 uvR = zoomUv(uvtR, 1.1); + float2 uvG = zoomUv(uvtG, 1.1); + float2 uvB = zoomUv(uvtB, 1.1); + + float4 colR = image.Sample(textureSampler, uvR); + float4 colG = image.Sample(textureSampler, uvG); + float4 colB = image.Sample(textureSampler, uvB); - hue += time; - hue = frac(hue); - float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); - float4 rgba = HSLtoRGB(hsl); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; - float4 luma_color = float4(luma, luma, luma, luma); - if (Replace_Image_Color) - color = luma_color; - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - - } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } - return rgba; + return float4(colR.r, colG.g, colB.b, (colR.a + colG.a + colB.a) / 3.0); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -33361,36 +24568,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRainWindowShader { +function Get-OBSCircleMaskFilterShader { -[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] +[Alias('Set-OBSCircleMaskFilterShader','Add-OBSCircleMaskFilterShader')] param( -# Set the size of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('size')] -[Single] -$Size, -# Set the blurSize of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('blurSize')] -[Single] -$BlurSize, -# Set the trail_strength of OBSRainWindowShader -[Alias('trail_strength')] -[ComponentModel.DefaultBindingProperty('trail_strength')] -[Single] -$TrailStrength, -# Set the trail_color of OBSRainWindowShader -[Alias('trail_color')] -[ComponentModel.DefaultBindingProperty('trail_color')] -[Single] -$TrailColor, -# Set the speed of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('speed')] +# Set the Radius of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Radius')] [Single] -$Speed, -# Set the debug of OBSRainWindowShader -[ComponentModel.DefaultBindingProperty('debug')] +$Radius, +# Set the Circle_Offset_X of OBSCircleMaskFilterShader +[Alias('Circle_Offset_X')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_X')] +[Int32] +$CircleOffsetX, +# Set the Circle_Offset_Y of OBSCircleMaskFilterShader +[Alias('Circle_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Circle_Offset_Y')] +[Int32] +$CircleOffsetY, +# Set the Source_Offset_X of OBSCircleMaskFilterShader +[Alias('Source_Offset_X')] +[ComponentModel.DefaultBindingProperty('Source_Offset_X')] +[Int32] +$SourceOffsetX, +# Set the Source_Offset_Y of OBSCircleMaskFilterShader +[Alias('Source_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Source_Offset_Y')] +[Int32] +$SourceOffsetY, +# Set the Antialiasing of OBSCircleMaskFilterShader +[ComponentModel.DefaultBindingProperty('Antialiasing')] [Management.Automation.SwitchParameter] -$DebugShader, +$Antialiasing, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33421,246 +24630,434 @@ $UseShaderTime process { -$shaderName = 'rain-window' -$ShaderNoun = 'OBSRainWindowShader' +$shaderName = 'circle-mask-filter' +$ShaderNoun = 'OBSCircleMaskFilterShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro -// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 -// https://www.shadertoy.com/view/ltffzl -// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// Circle Mask Filter version 1.01, for OBS Shaderfilter +// Copyright 2022 by SkeletonBow +// Twitter: +// Twitch: +// License: GNU GPLv2 +// +// Changelog: +// 1.01 - Don''t saturate() Radius parameter to allow oversizing to cover entire input texture. +// 1.0 - Initial release -uniform float size< - string label = "Rain Drop Size"; - string widget_type = "slider"; - float minimum = 0.001; - float maximum = 0.5; - float step = 0.01; -> = 0.2; -uniform float blurSize< - string label = "Blur Radius"; +uniform float Radius< + string label = "Radius"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = 0; float maximum = 100.0; float step = 0.01; -> = 32.0; // BLUR SIZE (Radius) -uniform float trail_strength< - string label = "Trail Strength"; +> = 50.0; +uniform int Circle_Offset_X< + string label = "Circle Offset X"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 100.0; -uniform float trail_color< - string label = "Trail Color"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Circle_Offset_Y< + string label = "Circle Offset X"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 40.0; -uniform float speed< - string label = "Speed"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0; +uniform int Source_Offset_X< + string label = "Source Offset X"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.01; -> = 100.0; -uniform bool debug = false; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; +uniform int Source_Offset_Y< + string label = "Source Offset Y"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +> = 0.0; +uniform bool Antialiasing = true; +#define Smoothness 100.00 +#define AAwidth 4 -float fract(float v){ - return v - floor(v); -} +#define uv_pi uv_pixel_interval -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); -} +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float2 coffset = float2(Circle_Offset_X, Circle_Offset_Y)/uv_size; + float2 soffset = float2( Source_Offset_X, Source_Offset_Y )/uv_size; -float3 fract3(float3 v){ - return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); + float radius = Radius * 0.01; + float smwidth = radius * Smoothness * 0.01; + + float4 obstex = image.Sample( textureSampler, uv - soffset); + float4 color = obstex; + // Account for aspect ratio + uv.x = (uv.x - 0.5) * uv_size.x / uv_size.y + 0.5; + float2 cuv = 0.5 + coffset; + float dist = distance(cuv,uv); + // Anti-aliased or pixelated edge + if( Antialiasing ) { + color.a = smoothstep( radius, (radius+(uv_pi.x)) - (uv_pi.x * AAwidth), dist); + } else { + color.a = step( dist, radius ); + } + + return float4(color.rgb, color.a); } -float3 fract4(float4 v){ - return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' } +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } -float3 N13(float p) { - // from DAVE HOSKINS - float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); - p3 += dot(p3, p3.yzx + 19.19); - return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); -} + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } -float4 N14(float t) { - return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); -} -float N(float t) { - return fract(sin(t*12345.564)*7658.76); + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } } -float Saw(float b, float t) { - return smoothstep(0., b, t)*smoothstep(1., b, t); } -float2 Drops(float2 uv, float t) { - - float2 UV = uv; - - // DEFINE GRID - uv.y += t*0.8; - float2 a = float2(6., 1.); - float2 grid = a*2.; - float2 id = floor(uv*grid); - - // RANDOM SHIFT Y - float colShift = N(id.x); - uv.y += colShift; - - // DEFINE SPACES - id = floor(uv*grid); - float3 n = N13(id.x*35.2+id.y*2376.1); - float2 st = fract2(uv*grid)-float2(.5, 0); - - // POSITION DROPS - //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 - float x = n.x-.5; - - float y = UV.y*20.; - - float distort = sin(y+sin(y)); - x += distort*(.5-abs(x))*(n.z-.5); - x *= .7; - float ti = fract(t+n.z); - y = (Saw(.85, ti)-.5)*.9+.5; - float2 p = float2(x, y); - - // DROPS - float d = length((st-p)*a.yx); - - float dSize = size; - - float Drop = smoothstep(dSize, .0, d); - - - float r = sqrt(smoothstep(1., y, st.y)); - float cd = abs(st.x-x); - - // TRAILS - float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); - float trailFront = smoothstep(-.02, .02, st.y-y); - trail *= trailFront; - - - // DROPLETS - y = UV.y; - y += N(id.x); - float trail2 = smoothstep(dSize*r, .0, cd); - float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; - y = fract(y*10.)+(st.y-.5); - float dd = length(st-float2(x, y)); - droplets = smoothstep(dSize*N(id.x), 0., dd); - float m = Drop+droplets*r*trailFront; - if(debug){ - m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES - } - - - return float2(m, trail); -} -float StaticDrops(float2 uv, float t) { - uv *= 30.; - - float2 id = floor(uv); - uv = fract2(uv)-.5; - float3 n = N13(id.x*107.45+id.y*3543.654); - float2 p = (n.xy-.5)*0.5; - float d = length(uv-p); - - float fade = Saw(.025, fract(t+n.z)); - float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; +} - return c; + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSClockAnalogShader { + +[Alias('Set-OBSClockAnalogShader','Add-OBSClockAnalogShader')] +param( +# Set the current_time_ms of OBSClockAnalogShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] +[Int32] +$CurrentTimeMs, +# Set the current_time_sec of OBSClockAnalogShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockAnalogShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockAnalogShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the hour_handle_color of OBSClockAnalogShader +[Alias('hour_handle_color')] +[ComponentModel.DefaultBindingProperty('hour_handle_color')] +[Single[]] +$HourHandleColor, +# Set the minute_handle_color of OBSClockAnalogShader +[Alias('minute_handle_color')] +[ComponentModel.DefaultBindingProperty('minute_handle_color')] +[Single[]] +$MinuteHandleColor, +# Set the second_handle_color of OBSClockAnalogShader +[Alias('second_handle_color')] +[ComponentModel.DefaultBindingProperty('second_handle_color')] +[Single[]] +$SecondHandleColor, +# Set the outline_color of OBSClockAnalogShader +[Alias('outline_color')] +[ComponentModel.DefaultBindingProperty('outline_color')] +[Single[]] +$OutlineColor, +# Set the top_line_color of OBSClockAnalogShader +[Alias('top_line_color')] +[ComponentModel.DefaultBindingProperty('top_line_color')] +[Single[]] +$TopLineColor, +# Set the background_color of OBSClockAnalogShader +[Alias('background_color')] +[ComponentModel.DefaultBindingProperty('background_color')] +[Single[]] +$BackgroundColor, +# Set the time_offset_hours of OBSClockAnalogShader +[Alias('time_offset_hours')] +[ComponentModel.DefaultBindingProperty('time_offset_hours')] +[Int32] +$TimeOffsetHours, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'clock_analog' +$ShaderNoun = 'OBSClockAnalogShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Based on https://www.shadertoy.com/view/XdKXzy +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform float3 hour_handle_color = {1.0,1.0,1.0}; +uniform float3 minute_handle_color = {1.0,1.0,1.0}; +uniform float3 second_handle_color = {1.0,0.0,0.0}; +uniform float3 outline_color = {1.0,1.0,1.0}; +uniform float3 top_line_color = {1.0,0.0,0.0}; +uniform float3 background_color = {.5,.5,.5}; +uniform int time_offset_hours = 0; + +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif +// this is my first try to actually use glsl almost from scratch +// so far all i''ve done is learning by doing / reading glsl docs. +// this is inspired by my non glsl „elapsed_time“ projects +// especially this one: https://www.gottz.de/analoguhr.htm + +// i will most likely use a buffer in future to calculate the elapsed_time +// aswell as to draw the background of the clock only once. +// tell me if thats a bad idea. + +// update: +// screenshot: http://i.imgur.com/dF0nHDk.png +// as soon as i think its in a usefull state i''ll release the source +// of that particular c++ application on github. +// i hope sommeone might find it usefull :D + +#define PI 3.141592653589793238462643383 + +// from https://www.shadertoy.com/view/4s3XDn <3 +float ln(float2 p, float2 a, float2 b) +{ + float2 pa = p - a; + float2 ba = b - a; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h); } -float2 Rain(float2 uv, float t) { - //float s = StaticDrops(uv, t); - float2 r1 = Drops(uv, t); - float2 r2 = Drops(uv*1.8, t); - float c; - if(debug){ - c = r1.x; - }else{ - c = r1.x+r2.x;//s+r1.x+r2.x; - } - - c = smoothstep(.3, 1., c); +// i think i should spend some elapsed_time reading docs in order to minimize this. +// hints apreciated +// (Rotated LiNe) +float rln(float2 uv, float start, float end, float perc) { + float inp = perc * PI * 2.0; + float2 coord = float2(sin(inp), cos(inp)); + return ln(uv, coord * start, coord * end); +} + +// i need this to have an alphachannel in the output +// i intend to use an optimized version of this shader for a transparent desktop widget experiment +float4 mixer(float4 c1, float4 c2) { + // please tell me if you think this would boost performance. + // the elapsed_time i implemented mix myself it sure did reduce + // the amount of operations but i''m not sure now + // if (c2.a <= 0.0) return c1; + // if (c2.a >= 1.0) return c2; + return float4(lerp(c1.rgb, c2.rgb, c2.a), c1.a + c2.a); + // in case you are curious how you could implement mix yourself: + // return float4(c2.rgb * c2.a + c1.rgb * (1.0-c2.a), c1.a+c2.a); +} - if(debug){ - return float2(c, r1.y); - }else{ - return float2(c, max(r1.y, r2.y)); +float4 styleHandle(float4 color, float px, float dist, float3 handleColor, float width, float shadow) { + if (dist <= width + shadow) { + // lets draw the shadow + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0-pow(smoothstep(width, width + shadow, dist),0.2))*0.2)); + // now lets draw the antialiased handle + color = mixer(color, float4(handleColor, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); } + return color; } float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; - uv.y = 1.0 - uv.y; - uv = uv * uv_scale; - float2 UV = v_in.uv; - float T = elapsed_time * speed / 100.0; - - float t = T*.2; + float2 R = uv_size; + // calculate the size of a pixel + float px = 1.0 / R.y; + // create percentages of the coordinate system + float2 p = (v_in.uv * uv_size).xy / R; + // center the scene and add perspective + float2 uv = (2.0 * (float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size) - R) / min(R.x, R.y); - UV = (UV-.5)*(.9)+.5; - - float2 c = Rain(uv, t); - - float2 e = float2(.001, 0.); //pixel offset - float cx = Rain(uv+e, t).x; - float cy = Rain(uv+e.yx, t).x; - float2 n = float2(cx-c.x, cy-c.x); //normals - - // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj - float Pi = 6.28318530718; // Pi*2 - - // GAUSSIAN BLUR SETTINGS {{{ - float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) - float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) - // GAUSSIAN BLUR SETTINGS }}} - float2 Radius = blurSize/uv_size; - float3 col = image.Sample(textureSampler, UV).rgb; - - if(blurSize > 0.0){ - // Blur calculations - for(float d=0.0; d uv_size.y) + uv.x *= uv_size.x / uv_size.y; + else + uv.y *= uv_size.y / uv_size.x;*/ + + // lets scale the scene a bit down: + uv *= 1.1; + px *= 0.9; + + float width = 0.015; + float dist = 1.0; + float centerdist = length(uv); + + float4 color = image.Sample(textureSampler, v_in.uv); + + // background of the clock + if (centerdist < 1.0 - width) color = mixer(color, float4(background_color, 0.4*(1.8-length(uv)))); + + float isRed = 1.0; + + if (centerdist > 1.0 - 12.0 * width && centerdist <= 1.1) { + // minute bars + for (float i = 0.0; i <= 15.0; i += 1.0) { + if (mod(i, 5.0) == 0.0) { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 2.0 * width, i / 60.0)); + // draw first bar red + if (i == 0.0 && uv.y > 0.0) { + isRed = dist; + dist = smoothstep(width, max(width - 3.0 * px, 0.0), dist); + color = mixer(color, float4(top_line_color, dist)); + dist = 1.0; } - float3 t = image.Sample(textureSampler, uv2).rgb; - col = col + t; + } + else { + dist = min(dist, rln(abs(uv), 1.0 - 10.0 * width, 1.0 - 7.0 * width, i / 60.0)); } } - col /= Quality * Directions - 0.0; - } + // outline circle + dist = min(dist, abs(1.0-width-length(uv))); + // draw clock shadow + if (centerdist > 1.0) + color = mixer(color, float4(0.0,0.0,0.0, 0.3*smoothstep(1.0 + width*2.0, 1.0, centerdist))); - float3 tex = image.Sample(textureSampler, UV+n).rgb; + // draw outline + minute bars in white + color = mixer(color, float4(0.0, 0.0, 0.0, + (1.0 - pow(smoothstep(width, width + 0.02, min(isRed, dist)), 0.4))*0.2)); + color = mixer(color, float4(outline_color, smoothstep(width, max(width - 3.0 * px, 0.0), dist))); + } + + if (centerdist < 1.0) { + float elapsed_time = float((time_offset_hours+current_time_hour)*3600+current_time_min*60+current_time_sec) + pow(float(current_time_ms)/1000.0,16.0); + // hour + color = styleHandle(color, px, + rln(uv, -0.05, 0.5, elapsed_time / 3600.0 / 12.0), + hour_handle_color, 0.03, 0.02); - c.y = clamp(c.y, 0.0, 1.); - c.y = c.y * trail_strength /100.0; - col -= c.y; - col += c.y*(tex + 1.0 - trail_color/100.0); + // minute + color = styleHandle(color, px, + rln(uv, -0.075, 0.7, elapsed_time / 3600.0), + minute_handle_color, 0.02, 0.02); - return float4(col, 1.); + // second + color = styleHandle(color, px, + min(rln(uv, -0.1, 0.9, elapsed_time / 60.0), length(uv)-0.01), + second_handle_color, 0.01, 0.02); + } + + + return color; } ' } @@ -33759,30 +25156,53 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRectangularDropShadowShader { +function Get-OBSClockDigitalLedShader { -[Alias('Set-OBSRectangularDropShadowShader','Add-OBSRectangularDropShadowShader')] +[Alias('Set-OBSClockDigitalLedShader','Add-OBSClockDigitalLedShader')] param( -# Set the shadow_offset_x of OBSRectangularDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +# Set the current_time_sec of OBSClockDigitalLedShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] [Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSRectangularDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalLedShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] [Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSRectangularDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalLedShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] [Int32] -$ShadowBlurSize, -# Set the shadow_color of OBSRectangularDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the showMatrix of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showMatrix')] +[Management.Automation.SwitchParameter] +$ShowMatrix, +# Set the showOff of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('showOff')] +[Management.Automation.SwitchParameter] +$ShowOff, +# Set the ampm of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ampm')] +[Management.Automation.SwitchParameter] +$Ampm, +# Set the ledColor of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('ledColor')] [String] -$ShadowColor, +$LedColor, +# Set the offsetHours of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalLedShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -33813,261 +25233,214 @@ $UseShaderTime process { -$shaderName = 'rectangular_drop_shadow' -$ShaderNoun = 'OBSRectangularDropShadowShader' +$shaderName = 'clock_digital_led' +$ShaderNoun = 'OBSClockDigitalLedShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Exeldro February 22, 2022 -uniform int shadow_offset_x< - string label = "shadow offset x"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; ->; -uniform int shadow_offset_y< - string label = "shadow offset y"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; - string widget_type = "slider"; - int minimum = 1; - int maximum = 100; - int step = 1; -> = 1; +// based on https://www.shadertoy.com/view/MdfGzf +// cmarangu has linked all 7 segments in his comments +// https://www.shadertoy.com/view/3dtSRj -uniform float4 shadow_color; +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#endif -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); - float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); - float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); - - float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); - float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); - float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); - - float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); - - return final_shadow_color * (1-color.a) + color; -} +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } - - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } - - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } - - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } +uniform bool showMatrix = false; +uniform bool showOff = false; +uniform bool ampm = false; +uniform float4 ledColor = {1.0,0,0,1.0}; +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } - } +float segment(float2 uv, bool On) +{ + if (!On && !showOff) + return 0.0; + + float seg = (1.0-smoothstep(0.08,0.09+float(On)*0.02,abs(uv.x)))* + (1.0-smoothstep(0.46,0.47+float(On)*0.02,abs(uv.y)+abs(uv.x))); + + //Fiddle with lights and matrix + //uv.x += sin(elapsed_time*60.0*6.26)/14.0; + //uv.y += cos(elapsed_time*60.0*6.26)/14.0; + + //led like brightness + if (On){ + seg *= (1.0-length(uv*float2(3.8,0.9)));//-sin(elapsed_time*25.0*6.26)*0.04; + } else { + seg *= -(0.05+length(uv*float2(0.2,0.1))); + } + return seg; } +float sevenSegment(float2 uv,int num) +{ + float seg= 0.0; + seg += segment(uv.yx+float2(-1.0, 0.0),num!=-1 && num!=1 && num!=4 ); + seg += segment(uv.xy+float2(-0.5,-0.5),num!=-1 && num!=1 && num!=2 && num!=3 && num!=7); + seg += segment(uv.xy+float2( 0.5,-0.5),num!=-1 && num!=5 && num!=6 ); + seg += segment(uv.yx+float2( 0.0, 0.0),num!=-1 && num!=0 && num!=1 && num!=7 ); + seg += segment(uv.xy+float2(-0.5, 0.5),num==0 || num==2 || num==6 || num==8 ); + seg += segment(uv.xy+float2( 0.5, 0.5),num!=-1 && num!=2 ); + seg += segment(uv.yx+float2( 1.0, 0.0),num!=-1 && num!=1 && num!=4 && num!=7 ); + + return seg; } +float showNum(float2 uv,int nr, bool zeroTrim) +{ + //Speed optimization, leave if pixel is not in segment + if (abs(uv.x)>1.5 || abs(uv.y)>1.2) + return 0.0; + + float seg= 0.0; + if (uv.x>0.0) + { + nr /= 10; + if (nr==0 && zeroTrim) + nr = -1; + seg += sevenSegment(uv+float2(-0.75,0.0),nr); + } else { + seg += sevenSegment(uv+float2( 0.75,0.0),int(mod(float(nr),10.0))); + } -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSReflectShader { - -[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] -param( -# Set the Horizontal of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Horizontal')] -[Management.Automation.SwitchParameter] -$Horizontal, -# Set the Vertical of OBSReflectShader -[ComponentModel.DefaultBindingProperty('Vertical')] -[Management.Automation.SwitchParameter] -$Vertical, -# Set the center_x_percent of OBSReflectShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSReflectShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) - + return seg; +} -process { -$shaderName = 'Reflect' -$ShaderNoun = 'OBSReflectShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Reflect Shader +float dots(float2 uv) +{ + float seg = 0.0; + uv.y -= 0.5; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + uv.y += 1.0; + seg += (1.0-smoothstep(0.11,0.13,length(uv))) * (1.0-length(uv)*2.0); + return seg; +} -// Reflects horizontally and/or vertically. +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = ((float2(v_in.uv.x, 1.0-v_in.uv.y) * uv_size).xy-0.5*uv_size) / + min(uv_size.x,uv_size.y); + + if (uv_size.x>uv_size.y) + { + uv *= 6.0; + } + else + { + uv *= 12.0; + } + + uv.x *= -1.0; + uv.x += uv.y/12.0; + //wobble + //uv.x += sin(uv.y*3.0+elapsed_time*14.0)/25.0; + //uv.y += cos(uv.x*3.0+elapsed_time*14.0)/25.0; + uv.x += 3.5; + float seg = 0.0; -uniform bool Horizontal< - string label = "Reflect horizontally"; -> = false; -uniform bool Vertical< - string label = "Reflect vertically"; -> = true; + if(timeMode == 0){ + seg += showNum(uv,current_time_sec,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + seg += showNum(uv,current_time_min,false); + uv.x -= 1.75; + seg += dots(uv); + uv.x -= 1.75; + if (ampm) { + if(current_time_hour == 0){ + seg += showNum(uv,12,true); + }else if(current_time_hour > 12){ + seg += showNum(uv,current_time_hour-12,true); + }else{ + seg += showNum(uv,current_time_hour,true); + } + } else { + seg += showNum(uv,current_time_hour,true); + } + }else{ + float timeSecs = 0.0; + if(timeMode == 1){ + timeSecs = elapsed_time_enable; + }else if(timeMode == 2){ + timeSecs = elapsed_time_active; + }else if(timeMode == 3){ + timeSecs = elapsed_time_show; + }else if(timeMode == 4){ + timeSecs = elapsed_time_start; + } -uniform int center_x_percent< - string label = "center x percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; + timeSecs += offsetSeconds + offsetHours*3600; + if(timeSecs < 0) + timeSecs = 0.9999-timeSecs; + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + + uv.x -= 1.75; + seg += dots(uv); + + uv.x -= 1.75; + + seg += showNum(uv,int(mod(timeSecs,60.0)),false); + + timeSecs = floor(timeSecs/60.0); + if (ampm) + { + if(timeSecs == 0.0){ + timeSecs = 12.0; + }else if (timeSecs > 12.0){ + timeSecs = mod(timeSecs,12.0); + } + }else if (timeSecs > 24.0) { + timeSecs = mod(timeSecs,24.0); + } + + uv.x -= 1.75; + + seg += dots(uv); + + uv.x -= 1.75; + seg += showNum(uv,int(mod(timeSecs,60.0)),true); + } -float4 mainImage(VertData v_in) : TARGET -{ - float2 pos = v_in.uv; - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - - if (Horizontal == true) { - if (pos.x < center_pos.x) { - pos.x = center_pos.x - pos.x; - } else if (pos.x == center_pos.x) { - pos.x = pos.x; - } else { - pos.x = pos.x - center_pos.x; - } - } - if (Vertical == true) { - if (pos.y < center_pos.y) { - pos.y = center_pos.y - pos.y; - } else if (pos.y == center_pos.y) { - pos.y = pos.y; - } else { - pos.y = pos.y - center_pos.y; - } - } - - return image.Sample(textureSampler, pos); + + if (seg==0.0){ + return image.Sample(textureSampler, v_in.uv); + } + // matrix over segment + if (showMatrix) + { + seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y+uv.x,0.06025)); + //seg *= 0.8+0.2*smoothstep(0.02,0.04,mod(uv.y-uv.x,0.06025)); + } + if (seg<0.0) + { + seg = -seg;; + return float4(seg,seg,seg,1.0); + } + return float4(ledColor.rgb * seg, ledColor.a); } ' } @@ -34166,19 +25539,62 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRemovePartialPixelsShader { +function Get-OBSClockDigitalNixieShader { -[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] +[Alias('Set-OBSClockDigitalNixieShader','Add-OBSClockDigitalNixieShader')] param( -# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +# Set the current_time_ms of OBSClockDigitalNixieShader +[Alias('current_time_ms')] +[ComponentModel.DefaultBindingProperty('current_time_ms')] [Int32] -$MinimumAlphaPercent, -# Set the notes of OBSRemovePartialPixelsShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$CurrentTimeMs, +# Set the current_time_sec of OBSClockDigitalNixieShader +[Alias('current_time_sec')] +[ComponentModel.DefaultBindingProperty('current_time_sec')] +[Int32] +$CurrentTimeSec, +# Set the current_time_min of OBSClockDigitalNixieShader +[Alias('current_time_min')] +[ComponentModel.DefaultBindingProperty('current_time_min')] +[Int32] +$CurrentTimeMin, +# Set the current_time_hour of OBSClockDigitalNixieShader +[Alias('current_time_hour')] +[ComponentModel.DefaultBindingProperty('current_time_hour')] +[Int32] +$CurrentTimeHour, +# Set the timeMode of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('timeMode')] +[Int32] +$TimeMode, +# Set the offsetHours of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetHours')] +[Int32] +$OffsetHours, +# Set the offsetSeconds of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('offsetSeconds')] +[Int32] +$OffsetSeconds, +# Set the corecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('corecolor')] +[Single[]] +$Corecolor, +# Set the halocolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('halocolor')] +[Single[]] +$Halocolor, +# Set the flarecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('flarecolor')] +[Single[]] +$Flarecolor, +# Set the anodecolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodecolor')] +[Single[]] +$Anodecolor, +# Set the anodehighlightscolor of OBSClockDigitalNixieShader +[ComponentModel.DefaultBindingProperty('anodehighlightscolor')] +[Single[]] +$Anodehighlightscolor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34209,38 +25625,484 @@ $UseShaderTime process { -$shaderName = 'remove_partial_pixels' -$ShaderNoun = 'OBSRemovePartialPixelsShader' +$shaderName = 'clock_digital_nixie' +$ShaderNoun = 'OBSClockDigitalNixieShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGL by Exeldro February 21, 2022 -uniform int minimum_alpha_percent< - string label = "minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; +//based on https://www.shadertoy.com/view/fsBcRm +uniform int current_time_ms; +uniform int current_time_sec; +uniform int current_time_min; +uniform int current_time_hour; +uniform int timeMode< + string label = "Time mode"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Time"; + int option_1_value = 1; + string option_1_label = "Enable duration"; + int option_2_value = 2; + string option_2_label = "Active duration"; + int option_3_value = 3; + string option_3_label = "Show duration"; + int option_4_value = 4; + string option_4_label = "Load duration"; +> = 0; -float4 mainImage(VertData v_in) : TARGET +uniform int offsetHours = 0; +uniform int offsetSeconds = 0; + +// Colors as named variables, if you want to tweak them +uniform float3 corecolor = {1.0,0.7,0.0}; +uniform float3 halocolor = {1.0,0.5,0.0}; +uniform float3 flarecolor = {1.0,0.3,0.0}; +uniform float3 anodecolor = {0.2,0.1,0.1}; +uniform float3 anodehighlightscolor = {1.0,0.5,0.0}; + +#ifndef OPENGL +#define mod(x,y) (x - y * floor(x / y)) +#define lessThan(a,b) (a < b) +#define greaterThan(a,b) (a > b) +#endif + +// psrdnoise (c) Stefan Gustavson and Ian McEwan, +// ver. 2021-12-02, published under the MIT license: +// https://github.com/stegu/psrdnoise/ +float psrdnoise(float2 x, float2 period, float alpha, out float2 gradient) { - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - else - { - return float4(output_color); - } -} -' + float2 uv = float2(x.x+x.y*0.5, x.y); + float2 i0 = floor(uv), f0 = frac(uv); + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + float2 i1 = i0 + o1, i2 = i0 + 1.0; + float2 v0 = float2(i0.x - i0.y*0.5, i0.y); + float2 v1 = float2(v0.x + o1.x - o1.y*0.5, v0.y + o1.y); + float2 v2 = float2(v0.x + 0.5, v0.y + 1.0); + float2 x0 = x - v0, x1 = x - v1, x2 = x - v2; + float3 iu, iv, xw, yw; + if(any(greaterThan(period, float2(0.0,0.0)))) { + xw = float3(v0.x, v1.x, v2.x); + yw = float3(v0.y, v1.y, v2.y); + if(period.x > 0.0) + xw = mod(float3(v0.x, v1.x, v2.x), period.x); + if(period.y > 0.0) + yw = mod(float3(v0.y, v1.y, v2.y), period.y); + iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); + } else { + iu = float3(i0.x, i1.x, i2.x); iv = float3(i0.y, i1.y, i2.y); + } + float3 hash = mod(iu, 289.0); + hash = mod((hash*51.0 + 2.0)*hash + iv, 289.0); + hash = mod((hash*34.0 + 10.0)*hash, 289.0); + float3 psi = hash*0.07482 + alpha; + float3 gx = cos(psi); float3 gy = sin(psi); + float2 g0 = float2(gx.x, gy.x); + float2 g1 = float2(gx.y, gy.y); + float2 g2 = float2(gx.z, gy.z); + float3 w = 0.8 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); + w = max(w, 0.0); float3 w2 = w*w; float3 w4 = w2*w2; + float3 gdotx = float3(dot(g0, x0), dot(g1, x1), dot(g2, x2)); + float n = dot(w4, gdotx); + float3 w3 = w2*w; float3 dw = -8.0*w3*gdotx; + float2 dn0 = w4.x*g0 + dw.x*x0; + float2 dn1 = w4.y*g1 + dw.y*x1; + float2 dn2 = w4.z*g2 + dw.z*x2; + gradient = 10.9*(dn0 + dn1 + dn2); + return 10.9*n; +} + +// Compute the shortest distance from p +// to a line segment from p1 to p2. +float lined(float2 p1, float2 p2, float2 p) { + float2 p1p2 = p2 - p1; + float2 v = normalize(p1p2); + float2 s = p - p1; + float t = dot(v, s); + if (t<0.0) return length(s); + if (t>length(p1p2)) return length(p - p2); + return length(s - t*v); +} + +// Compute the shortest distance from p to a circle +// with center at c and radius r. (Extremely simple.) +float circled(float2 c, float r, float2 p) { + return abs(length(p - c) - r); +} + +// Compute the shortest distance from p to a +// circular arc with center c from p1 to p2. +// p1, p2 are in the +angle direction (ccw), +// to resolve the major/minor arc ambiguity, so +// specifying p1, p2 in the wrong order will +// yield the complement to the arc you wanted. +// If p1 = p2, the entire circle is drawn, but +// you don''t want to use this function to draw +// a circle. Use the simple circled() instead. +// If p1 and p2 have different distances to c, +// the end of the arc will not look right. If +// this is inconvenient, uncomment the 3rd line. +float arcd(float2 c, float2 p1, float2 p2, float2 p) { + + float2 v1 = p1 - c; + float2 v2 = p2 - c; + // Optional: make sure p1, p2 are both on the circle + // v2 = normalize(v2)*length(v1); + float2 v = p - c; + + float2 w = float2(dot(v, -float2(-v1.y, v1.x)), dot(v, float2(-v2.y, v2.x))); + + if(dot(v1, float2(-v2.y, v2.x)) >= 0.0) { // Arc angle <= pi + if(all(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); // nearest end + } else { + return abs(length(v) - length(v1)); // dist to arc + } + } else { // Arc angle > pi + if(any(lessThan(float2(0.0,0.0), w))) { + return min(length(p1-p), length(p2-p)); + } else { + return abs(length(v) - length(v1)); + } + } +} + +// A convenient anti-aliased step() using auto derivatives +float aastep(float threshold, float value) { + float afwidth = 0.7 * length(float2(ddx(value), ddy(value))); + return smoothstep(threshold-afwidth, threshold+afwidth, value); +} + +// A smoothstep() that blends to an aastep() under minification +float aasmoothstep(float t1, float t2, float v) { + float aw = 0.7 * length(float2(ddx(v), ddy(v))); + float sw = max(0.5*(t2-t1), aw); + float st = 0.5*(t1+t2); + return smoothstep(st-sw, st+sw, v); +} + +// Distance field of a hexagonal (simplex) grid +// The return vector contains the distances to the +// three closest points, sorted by magnitude. +float3 hexgrid(float2 p) { + + const float stretch = 1.0/0.8660; + + // v.y = v.y + 0.0001; // needed if no stretching (rounding errors) + p.y = p.y * stretch; + // Transform to grid space (axis-aligned "simplex" grid) + float2 uv = float2(p.x + p.y*0.5, p.y); + // Determine which simplex we''re in, with i0 being the "base" + float2 i0 = floor(uv); + float2 f0 = frac(uv); + // o1 is the offset in simplex space to the second corner + float cmp = step(f0.y, f0.x); + float2 o1 = float2(cmp, 1.0-cmp); + // Enumerate the remaining simplex corners + float2 i1 = i0 + o1; + float2 i2 = i0 + float2(1.0, 1.0); + // Transform corners back to texture space + float2 p0 = float2(i0.x - i0.y * 0.5, i0.y); + float2 p1 = float2(p0.x + o1.x - o1.y * 0.5, p0.y + o1.y); + float2 p2 = float2(p0.x + 0.5, p0.y + 1.0); + float3 d = float3(length(p-p0), length(p-p1), length(p-p2)); + // Only three values - bubble sort is just fine. + d.yz = (d.y < d.z) ? d.yz : d.zy; + d.xy = (d.x < d.y) ? d.xy : d.yx; + d.yz = (d.y < d.z) ? d.yz : d.zy; + return d; +} + +// The digits. Simple functions, only a lot of them. + +// These glyphs and their implementation as distance fields +// are the original work of me (stefan.gustavson@gmail.com), +// and the code below is released under the MIT license: +// https://opensource.org/licenses/MIT +// (If that is inconvenient for you, let me know. I''m reasonable.) +// +// Experts say mortals should not attempt to design character shapes. +// "It''s just ten simple digits", I thought, "How hard can it be?" +// A week later, after countless little tweaks to proportions and +// curvature, and with a notepad full of sketches and pen-and-paper +// math, some of it horribly wrong because it was decades since I +// solved this kind of equations by hand, I know the answer: +// It can be *really* hard. But also loads of fun! +// +float nixie0(float2 p) { + // Special hack instead of pasting together arcs and lines + float d = lined(float2(2.0,2.0), float2(2.0, 6.0), p); + return abs(d - 2.0); +} + +float nixie1(float2 p) { + float d1 = lined(float2(2.0, 0.0), float2(2.0, 8.0), p); + float d2 = lined(float2(2.0, 8.0), float2(1.0, 6.0), p); + return min(d1, d2); +} + +float nixie1alt(float2 p) { // Straight line + return lined(float2(2.0, 0.0), float2(2.0, 8.0), p); +} + +float nixie2(float2 p) { + const float x = 3.2368345; // Icky coordinates, + const float y = 4.4283002; // used twice below + float d1 = lined(float2(4.25, 0.0), float2(-0.25, 0.0), p); + float d2 = arcd(float2(10.657842, -5.001899), // Also icky + float2(x, y), float2(-0.25, 0.0), p); + float d3 = arcd(float2(2.0, 6.0), float2(x, y), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} + +float nixie2alt(float2 p) { // Straight neck + float d1 = lined(float2(4.0, 0.0), float2(0.0,0.0), p); + float d2 = lined(float2(0.0,0.0), float2(3.6, 4.8), p); + float d3 = arcd(float2(2.0, 6.0), float2(3.6, 4.8), float2(0.0, 6.0), p); + return min(min(d1, d2), d3); +} + +float nixie3(float2 p) { + // Two round parts: + // float d1 = arcd(float2(2.0, 2.1), float2(-0.1, 2.1), float2(2.0, 4.2), p); + // float d2 = arcd(float2(2.0, 6.1), float2(2.0, 4.2), float2(0.1, 6.1), p); + // Angled top, more like classic Nixie tube digits: + float d1 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + float d2 = lined(float2(2.0, 4.5), float2(4.0, 7.75), p); + float d3 = lined(float2(4.0, 7.75), float2(0.0, 7.75), p); + return min(min(d1, d2), d3); +} + +float nixie3alt(float2 p) { // Two round parts of the same size + float d1 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(2.0, 4.0), p); + float d2 = arcd(float2(2.0, 6.0), float2(2.0, 4.0), float2(0.0, 6.0), p); + return min(d1, d2); +} + +float nixie4(float2 p) { + // This digit is 5.0 units wide, most others are 4.0 or 4.5 + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(5.0, 2.0), p); + return min(min(d1, d2), d3); +} + +float nixie4alt(float2 p) { + // This digit is 4.0 units wide, but looks cropped + float d1 = lined(float2(4.0, 0.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(0.0, 2.0), p); + float d3 = lined(float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(d1, d2), d3); +} + +float nixie5(float2 p) { + float d1 = lined(float2(4.0, 7.75), float2(0.5, 7.75), p); + float d2 = lined(float2(0.5, 7.75), float2(0.0, 4.5), p); + float d3 = lined(float2(0.0, 4.5), float2(2.0, 4.5), p); + float d4 = arcd(float2(2.0, 2.25), float2(-0.25, 2.25), float2(2.0, 4.5), p); + return min(min(d1, d2), min(d3, d4)); +} + +float nixie5alt(float2 p) { + float d1 = lined(float2(4.0, 8.0), float2(0.0, 8.0), p); + float d2 = lined(float2(0.0, 8.0), float2(0.0, 5.0), p); + float d3 = lined(float2(0.0, 5.0), float2(2.0, 5.0), p); + float d4 = arcd(float2(2.0, 3.0), float2(4.0, 3.0), float2(2.0, 5.0), p); + float d5 = lined(float2(4.0, 3.0), float2(4.0, 2.0), p); + float d6 = arcd(float2(2.0,2.0), float2(0.0, 2.0), float2(4.0, 2.0), p); + return min(min(min(d1, d2), min(d3, d4)), min(d5, d6)); +} + +float nixie6(float2 p) { + float d1 = arcd(float2(84.0/13.0, 2.25), float2(3.0, 8.0), float2(-0.25, 2.25), p); + float d2 = circled(float2(2.0, 2.25), 2.25, p); + return min(d1, d2); +} + +float nixie6alt(float2 p) { // Straight neck + float d1 = lined(float2(0.4, 3.2), float2(3.0, 8.0), p); + float d2 = circled(float2(2.0,2.0), 2.0, p); + return min(d1, d2); +} + +float nixie7(float2 p) { // Ugly coordinates, but these expressions are exact + float d1 = lined(float2(0.0, 7.75), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d2 = arcd(float2(-8.0, 12.0), float2(2.5, 5.0), float2(0.25*sqrt(2259.0)-8.0, 7.75), p); + float d3 = arcd(float2(10.0, 0.0), float2(2.5, 5.0), float2(10.0-2.5*sqrt(13.0), 0.0), p); + return min(min(d1, d2), d3); +} + +float nixie7alt(float2 p) { // Straight neck + float d1 = lined(float2(0.0, 8.0), float2(4.0, 8.0), p); + float d2 = lined(float2(4.0, 8.0), float2(1.0, 0.0), p); + return min(d1, d2); +} + +float nixie8(float2 p) { + float d1 = circled(float2(2.0, 2.2), 2.2, p); + float d2 = circled(float2(2.0, 6.2), 1.8, p); + return min(d1, d2); +} + +float nixie8alt(float2 p) { // Same size loops + float d1 = circled(float2(2.0,2.0), 2.0, p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} + +float nixie9(float2 p) { + float d1 = arcd(float2(-32.0/13.0, 5.75), float2(1.0, 0.0), float2(4.25, 5.75), p); + float d2 = circled(float2(2.0, 5.75), 2.25, p); + return min(d1, d2); +} + +float nixie9alt(float2 p) { // Straight neck + float d1 = lined(float2(3.6, 4.8), float2(1.0, 0.0), p); + float d2 = circled(float2(2.0, 6.0), 2.0, p); + return min(d1, d2); +} + +float nixieminus(float2 p) { + return lined(float2(0.5, 4.0), float2(3.5, 4.0), p); +} + +float nixieequals(float2 p) { + float d1 = lined(float2(0.5, 3.0), float2(3.5, 3.0), p); + float d2 = lined(float2(0.5, 5.0), float2(3.5, 5.0), p); + return min(d1, d2); +} + +float nixieplus(float2 p) { + float d1 = lined(float2(0.0, 4.0), float2(4.0, 4.0), p); + float d2 = lined(float2(2.0, 2.0), float2(2.0, 6.0), p); + return min(d1, d2); +} + +float nixiedot(float2 p) { + // circled with r=0 yields a point, but with more work + return length(p - float2(2.0, 0.0)); +} + +float nixiecolon(float2 p) { + float d1 = length(p - float2(2.0,2.0)); + float d2 = length(p - float2(2.0, 5.0)); + return min(d1, d2); +} + +// End of MIT-licensed code + +float number(int n, float2 p) { + switch(n) { + case 0: return nixie0(p); + case 1: return nixie1(p); + case 2: return nixie2(p); + case 3: return nixie3(p); + case 4: return nixie4(p); + case 5: return nixie5(p); + case 6: return nixie6(p); + case 7: return nixie7(p); + case 8: return nixie8(p); + case 9: return nixie9(p); + default: return 1e10; + } +} +// Display the current time with a retro Nixie-tube look +// Stefan Gustavson (stegu on shadertoy.com) 2022-01-26 +// All code in the "Image" tab is public domain. +// Functions in the "Common" tab are also public domain, +// except where a separate license is specified. +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = ((float2(v_in.uv.x,1.0-v_in.uv.y) * uv_size)/uv_size.x); + float time = 0.0; + if(timeMode == 0){ + time = float(current_time_hour*3600+current_time_min*60+current_time_sec) + float(current_time_ms)/1000.0; + }else if(timeMode == 1){ + time = elapsed_time_enable; + }else if(timeMode == 2){ + time = elapsed_time_active; + }else if(timeMode == 3){ + time = elapsed_time_show; + }else if(timeMode == 4){ + time = elapsed_time_start; + } + time += offsetSeconds + offsetHours * 3600; + if(time < 0) + time = 0.9999-time; + float2 p = -3.0+uv*50.0 - float2(0.0,9.0); + + float bbox = 1.0-max(max(1.0-aastep(-3.0, p.x), aastep(47.0, p.x)), + max(1.0-aastep(-3.0, p.y), aastep(11.0, p.y))); + + // Some relief for the GPU: exit early if we''re in the black margins + if(bbox == 0.0) { + return float4(float3(0.0,0.0,0.0),1.0); + } + + // If not, well, let''s put that GPU to good use! + float secs = floor(mod(time, 60.0)); + float mins = floor(mod(time, 3600.0)/60.0); + float hrs = floor(time/3600.0); + int h10 = int(floor(hrs/10.0)); + int h1 = int(floor(mod(hrs, 10.0))); + int m10 = int(floor(mins/10.0)); + int m1 = int(floor(mod(mins, 10.0))); + int s10 = int(floor(secs/10.0)); + int s1 = int(floor(mod(secs, 10.0))); + + float2 wspace = float2(6.5, 0.0); + float2 nspace = float2(3.5, 0.0); + float d = 1e10; + d = min(d, number(h10, p)); + d = min(d, number(h1, p-wspace)); + d = min(d, nixiecolon(p-wspace-1.45*nspace)-0.2); + d = min(d, number(m10, p-2.0*wspace-nspace)); + d = min(d, number(m1, p-3.0*wspace-nspace)); + d = min(d, nixiecolon(p-3.0*wspace-2.4*nspace)-0.2); + d = min(d, number(s10, p-4.0*wspace-2.0*nspace)); + d = min(d, number(s1, p-5.0*wspace-2.0*nspace)); + + float2 g; // For gradients returned from psrdnoise() + + // Digit outlines + float core = 1.0-aastep(0.2, d); + // "flare" is a wide blurry region around the characters, and + // "flarenoise" is a spatio-temporal modulation of its extents + // (slight flickering, but not all characters at the same time) + float flarenoise = psrdnoise(float2(p.x*0.1,5.0*elapsed_time), float2(0.0,0.0), 0.0, g); + float flare = 1.0-smoothstep(0.0, 2.5, d + 0.05*flarenoise); + flare *= flare; // A more rapid decline towards the edge + // "glow" is a variation in the intensity of the glowy cathode (core) + float glow = 0.8+0.2*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 4.0*time, g); + // Now we mess up the distance field a little for the "halo" effect + d += 0.1*psrdnoise(p - float2(0.0, 2.0*elapsed_time), float2(0.0,0.0), 8.0*time, g); + d += 0.05*psrdnoise(2.0*p - float2(0.0, 4.0*elapsed_time) + 0.15*g, float2(0.0,0.0), -16.0*time, g); + // "halo" is a kind of flame/plasma cloud near the core. A real Nixie tube + // doesn''t have this, but it adds some appealing visual detail. + // Looks more like hot filaments than "cold cathodes", but... oh, well. + float halo = 1.0-smoothstep(-0.3, 0.3, d); + + // Brittle parameters! This scale/shift of p has a strong impact + // on the pattern at the edges of the grid through "anodefade". + float3 anodedists = hexgrid(1.7*p+float2(0.1,0.23)); + float anodedist = anodedists.y - anodedists.x; // Voronoi cell borders + // Fade the hexagonal holes in the anode towards the edges + float anodefade = max(max(1.0-aasmoothstep(-2.2, -1.5, p.x), aasmoothstep(45.5, 46.2, p.x)), + max(1.0-aasmoothstep(-2.0, -1.6, p.y), aasmoothstep(9.4, 10.0, p.y))); + float anode = 1.0 - aastep(0.1, anodedist - anodefade); + + float anodecolornoise = 0.02*psrdnoise(p*float2(0.2,2.0), float2(0.0,0.0), 0.0, g); + float3 anodecolorresult = anodecolor+ anodecolornoise*anodehighlightscolor; // Long variable names, I know + + float3 mixcolor = float3(0.0,0.0,0.0); // Mix additively from black + mixcolor = lerp(mixcolor, flarecolor, 0.5*flare); + mixcolor = lerp(mixcolor, halocolor, 0.9*halo); + mixcolor = lerp(mixcolor, corecolor, core*glow); + mixcolor = lerp(mixcolor, anodecolorresult, anode); + mixcolor *= bbox; // AA-mask to black at the very edge of the bounding box + return float4(mixcolor,1.0); +} + +' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { @@ -34337,30 +26199,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatGridCenterCropShader { +function Get-OBSColorDepthShader { -[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] +[Alias('Set-OBSColorDepthShader','Add-OBSColorDepthShader')] param( -# Set the ViewProj of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the alpha of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('alpha')] -[Single] -$Alpha, -# Set the columns of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('columns')] +# Set the colorDepth of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('colorDepth')] [Single] -$Columns, -# Set the rows of OBSRepeatGridCenterCropShader -[ComponentModel.DefaultBindingProperty('rows')] +$ColorDepth, +# Set the pixelSize of OBSColorDepthShader +[ComponentModel.DefaultBindingProperty('pixelSize')] [Single] -$Rows, +$PixelSize, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34391,60 +26241,40 @@ $UseShaderTime process { -$shaderName = 'repeat_grid_center_crop' -$ShaderNoun = 'OBSRepeatGridCenterCropShader' +$shaderName = 'color-depth' +$ShaderNoun = 'OBSColorDepthShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// repeat_grid_center_crop.effect - -uniform float4x4 ViewProj; -uniform texture2d image; -sampler_state def_sampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -uniform float alpha = 1.0; -uniform float columns = 3.0; -uniform float rows = 1.0; - -struct VertInOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +//based on https://www.shadertoy.com/view/tscfWM +uniform float colorDepth< + string label = "Color depth"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 100.0; + float step = 0.01; +> = 5.0; -VertInOut VSDefault(VertInOut vert_in) { - VertInOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1), ViewProj); - vert_out.uv = vert_in.uv * float2(columns, rows); - return vert_out; -} +uniform float pixelSize< + string label = "Pixel Size"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.01; +> = 5.0; -float4 PSMain(VertInOut vert_in) : TARGET { - // Calculate fractional UV within grid cell - float2 cell_uv = frac(vert_in.uv); - - // Calculate center crop parameters - float horizontalCropStart = 0.5 - 0.5/columns; - - // Map to centered portion of original image - float2 original_uv = float2( - cell_uv.x / columns + horizontalCropStart, - cell_uv.y / rows - ); - - // Sample the image - float4 col = image.Sample(def_sampler, original_uv); - col.a *= alpha; - return col; -} -technique Draw { - pass { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSMain(vert_in); - } +float4 mainImage(VertData v_in) : TARGET +{ + // Change these to change results + float2 size = uv_size / pixelSize; + float2 uv = v_in.uv; + // Maps UV onto grid of variable size to pixilate the image + uv = round(uv*size)/size; + float4 col = image.Sample(textureSampler, uv); + // Maps color onto the specified color depth + return float4(round(col.r * colorDepth) / colorDepth, + round(col.g * colorDepth) / colorDepth, + round(col.b * colorDepth) / colorDepth, 1.0); } ' } @@ -34543,75 +26373,33 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatShader { +function Get-OBSColorGradeFilterShader { -[Alias('Set-OBSRepeatShader','Add-OBSRepeatShader')] +[Alias('Set-OBSColorGradeFilterShader','Add-OBSColorGradeFilterShader')] param( -# Set the ViewProj of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the elapsed_time of OBSRepeatShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the alpha of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('alpha')] -[Single] -$Alpha, -# Set the copies of OBSRepeatShader -[ComponentModel.DefaultBindingProperty('copies')] -[Single] -$Copies, -# Set the notes of OBSRepeatShader +# Set the notes of OBSColorGradeFilterShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, +# Set the lut of OBSColorGradeFilterShader +[ComponentModel.DefaultBindingProperty('lut')] +[String] +$Lut, +# Set the lut_amount_percent of OBSColorGradeFilterShader +[Alias('lut_amount_percent')] +[ComponentModel.DefaultBindingProperty('lut_amount_percent')] +[Int32] +$LutAmountPercent, +# Set the lut_scale_percent of OBSColorGradeFilterShader +[Alias('lut_scale_percent')] +[ComponentModel.DefaultBindingProperty('lut_scale_percent')] +[Int32] +$LutScalePercent, +# Set the lut_offset_percent of OBSColorGradeFilterShader +[Alias('lut_offset_percent')] +[ComponentModel.DefaultBindingProperty('lut_offset_percent')] +[Int32] +$LutOffsetPercent, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34642,79 +26430,79 @@ $UseShaderTime process { -$shaderName = 'repeat' -$ShaderNoun = 'OBSRepeatShader' +$shaderName = 'color_grade_filter' +$ShaderNoun = 'OBSColorGradeFilterShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 - -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; - -uniform float alpha< - string label = "Alpha"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 4.0; +// Color Grade Filter by Charles Fettinger for obs-shaderfilter plugin 4/2020 +//https://github.com/Oncorporation/obs-shaderfilter +//OBS messed up the LUT system, this is basically the old LUT system +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 uniform string notes< string widget_type = "info"; -> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; +> = "Choose LUT, Default LUT amount is 100, scale = 100, offset = 0. Valid values: -200 to 200"; -sampler_state def_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; +uniform texture2d lut< + string label = "LUT"; +>; +uniform int lut_amount_percent< + string label = "LUT amount percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 100; +uniform int lut_scale_percent< + string label = "LUT scale percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 100; +uniform int lut_offset_percent< + string label = "LUT offset percentage"; + string widget_type = "slider"; + int minimum = -200; + int maximum = 200; + int step = 1; +> = 0; -struct VertInOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -VertInOut VSDefault(VertInOut vert_in) +float4 mainImage(VertData v_in) : TARGET { - VertInOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv = vert_in.uv * sqrt(copies); - return vert_out; -} + float lut_amount = clamp(lut_amount_percent *.01, -2.0, 2.0); + float lut_scale = clamp(lut_scale_percent *.01,-2.0, 2.0); + float lut_offset = clamp(lut_offset_percent *.01,-2.0, 2.0); -float4 PSDrawBare(VertInOut vert_in) : TARGET -{ - float4 rgba = image.Sample(def_sampler, vert_in.uv); - rgba.a *= alpha; - return rgba; -} + float4 textureColor = image.Sample(textureSampler, v_in.uv); + float lumaLevel = textureColor.r * 0.2126 + textureColor.g * 0.7152 + textureColor.b * 0.0722; + float blueColor = float(lumaLevel);//textureColor.b * 63.0 -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); - } -} + float2 quad1; + quad1.y = floor(floor(float(blueColor)) / 8.0); + quad1.x = floor(float(blueColor)) - (quad1.y * 8.0); + float2 quad2; + quad2.y = floor(ceil(float(blueColor)) / 8.0); + quad2.x = ceil(float(blueColor)) - (quad2.y * 8.0); + + float2 texPos1; + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float2 texPos2; + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); + texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); + + float4 newColor1 = lut.Sample(textureSampler, texPos1); + newColor1.rgb = newColor1.rgb * lut_scale + lut_offset; + float4 newColor2 = lut.Sample(textureSampler, texPos2); + newColor2.rgb = newColor2.rgb * lut_scale + lut_offset; + float4 luttedColor = lerp(newColor1, newColor2, frac(float(blueColor))); + float4 final_color = lerp(textureColor, luttedColor, lut_amount); + return float4(final_color.rgb, textureColor.a); +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -34812,85 +26600,55 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRepeatTextureShader { +function Get-OBSCornerPinShader { -[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] +[Alias('Set-OBSCornerPinShader','Add-OBSCornerPinShader')] param( -# Set the ViewProj of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the color_matrix of OBSRepeatTextureShader -[Alias('color_matrix')] -[ComponentModel.DefaultBindingProperty('color_matrix')] -[Single[][]] -$ColorMatrix, -# Set the color_range_min of OBSRepeatTextureShader -[Alias('color_range_min')] -[ComponentModel.DefaultBindingProperty('color_range_min')] -[Single[]] -$ColorRangeMin, -# Set the color_range_max of OBSRepeatTextureShader -[Alias('color_range_max')] -[ComponentModel.DefaultBindingProperty('color_range_max')] -[Single[]] -$ColorRangeMax, -# Set the image of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('image')] -[String] -$Image, -# Set the tex_image of OBSRepeatTextureShader -[Alias('tex_image')] -[ComponentModel.DefaultBindingProperty('tex_image')] -[String] -$TexImage, -# Set the elapsed_time of OBSRepeatTextureShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +# Set the Antialias_Edges of OBSCornerPinShader +[Alias('Antialias_Edges')] +[ComponentModel.DefaultBindingProperty('Antialias_Edges')] +[Management.Automation.SwitchParameter] +$AntialiasEdges, +# Set the Top_Left_X of OBSCornerPinShader +[Alias('Top_Left_X')] +[ComponentModel.DefaultBindingProperty('Top_Left_X')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSRepeatTextureShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRepeatTextureShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRepeatTextureShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the uv_size of OBSRepeatTextureShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the rand_f of OBSRepeatTextureShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$TopLeftX, +# Set the Top_Left_Y of OBSCornerPinShader +[Alias('Top_Left_Y')] +[ComponentModel.DefaultBindingProperty('Top_Left_Y')] [Single] -$RandF, -# Set the blend of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('blend')] +$TopLeftY, +# Set the Top_Right_X of OBSCornerPinShader +[Alias('Top_Right_X')] +[ComponentModel.DefaultBindingProperty('Top_Right_X')] [Single] -$Blend, -# Set the copies of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('copies')] +$TopRightX, +# Set the Top_Right_Y of OBSCornerPinShader +[Alias('Top_Right_Y')] +[ComponentModel.DefaultBindingProperty('Top_Right_Y')] [Single] -$Copies, -# Set the notes of OBSRepeatTextureShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the alpha_percentage of OBSRepeatTextureShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] +$TopRightY, +# Set the Bottom_Left_X of OBSCornerPinShader +[Alias('Bottom_Left_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] [Single] -$AlphaPercentage, +$BottomLeftX, +# Set the Bottom_Left_Y of OBSCornerPinShader +[Alias('Bottom_Left_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +[Single] +$BottomLeftY, +# Set the Bottom_Right_X of OBSCornerPinShader +[Alias('Bottom_Right_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +[Single] +$BottomRightX, +# Set the Bottom_Right_Y of OBSCornerPinShader +[Alias('Bottom_Right_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +[Single] +$BottomRightY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -34921,108 +26679,196 @@ $UseShaderTime process { -$shaderName = 'repeat_texture' -$ShaderNoun = 'OBSRepeatTextureShader' +$shaderName = 'corner-pin' +$ShaderNoun = 'OBSCornerPinShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 - -uniform float4x4 ViewProj; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; -uniform texture2d image; -uniform texture2d tex_image; +// Corner Pin, Version 0.1, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Based on: +// Original work by Inigo Quilez: https://www.iquilezles.org/www/articles/ibilinear/ibilinear.htm +// https://www.youtube.com/c/InigoQuilez +// https://iquilezles.org/ +// and the derivative StreamFX Corner Pin effect by Xaymar +// https://github.com/Xaymar/obs-StreamFX +// +// Description: +// Corner Pin allows you to pin the corners of an image onto the corners of an arbitrarily +// angled picture frame, TV screen or other rectangular surface in 3D space in an underlying +// image with proper perspective without distortion. +// +// TODO: +// - Add feature to automatically quantize corners to pixel centers +// +// Changelog: +// 0.1 - Initial release based on the StreamFX Corner Pin effect, as well as the original work by +// Inigo Quilez that it was based upon. -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float2 uv_size; -uniform float rand_f; +uniform bool Antialias_Edges = true; -uniform float blend< - string label = "Blend"; +uniform float Top_Left_X< + string label = "Top left x"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 3.0; - float step = 0.001; -> = 1.0; -uniform float copies< - string label = "Copies"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Top_Left_Y< + string label = "Top left y"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = -100.0; float maximum = 100.0; float step = 0.1; -> = 4.0; -uniform string notes< - string widget_type = "info"; -> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; -uniform float alpha_percentage< - string label = "alpha percentage"; +> = -100.; +uniform float Top_Right_X< + string label = "Top right x"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = -100.0; float maximum = 100.0; float step = 0.1; -> = 100.0; - -sampler_state tex_sampler { - Filter = Linear; - AddressU = Repeat; - AddressV = Repeat; -}; - -sampler_state base_sampler { - Filter = Linear; - AddressU = Clamp; - AddressV = Clamp; -}; - -struct VertIn { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; +> = 100.; +uniform float Top_Right_Y< + string label = "Top right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_X< + string label = "Bottom left x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = -100.; +uniform float Bottom_Left_Y< + string label = "Bottom left y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom right x"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom right y"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.1; +> = 100.; -struct VertOut { - float4 pos : POSITION; - float2 uv_0 : TEXCOORD0; - float2 uv_1 : TEXCOORD1; -}; +// DEVELOPMENTAL DEBUGGING OPTIONS +//uniform float AAstrength = 1.0; +//uniform float AAdist = 1.0; +//uniform float debug_psmult = 1.0; +//#define PIXEL_SIZE_MULT 2.0 -VertOut VSDefault(VertIn vert_in) +float cross2d(in float2 a, in float2 b) { - VertOut vert_out; - vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); - vert_out.uv_1 = vert_in.uv_0; - vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); - return vert_out; + return (a.x * b.y) - (a.y * b.x); } -float4 PSDrawBare(VertOut vert_in) : TARGET +float2 inverse_bilinear(in float2 p, in float2 a, in float2 b, in float2 c, in float2 d) { - float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); - float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); - float4 base = image.Sample(base_sampler, vert_in.uv_1); + float2 result = float2(-1., -1.); - return (1 - alpha) * base + (alpha) * tex; -} + float2 e = b - a; + float2 f = d - a; + float2 g = a-b+c-d; + float2 h = p-a; -technique Draw -{ - pass - { - vertex_shader = VSDefault(vert_in); - pixel_shader = PSDrawBare(vert_in); + 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; } -' +// distance to a line segment +float sdSegment( in float2 p, in float2 a, in float2 b ) +{ + p -= a; b -= a; + return length( p-b*saturate(dot(p,b)/dot(b,b)) ); } -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' + +// Anti-alias edges - EXPERIMENTAL - (SkeletonBow) +float aastepEdgeAlpha(in float alpha, in float2 p, in float2 a, in float2 b) +{ + //float ps = 2.0 * (2.0/uv_size.y); // Original +// float ps = debug_psmult * (2.0/uv_size.y); + float ps = (2.0/uv_size.y); +// float ps = fwidth(p)*2.; // Try using fwidth() - goes haywire on AMD Radeon HD7850 at least, disable for now + return lerp( alpha, 0.0, 1.0 - smoothstep(0.0,ps,sdSegment(p,a,b))); +} + +float4 mainImage( VertData v_in ) : TARGET { + float2 p = 2.* v_in.uv - 1.; + + float2 Top_Left = float2(Top_Left_X, Top_Left_Y) * .01; + float2 Top_Right = float2(Top_Right_X, Top_Right_Y) * .01; + float2 Bottom_Left = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 Bottom_Right = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + // Convert from screen coords to potential Quad UV coordinates + float2 uv = inverse_bilinear(p, Top_Left, Top_Right, Bottom_Right, Bottom_Left); + + if (max(abs(uv.x - .5), abs(uv.y - .5)) >= .5) { + return float4(0.0, 0.0, 0.0, 0.0); + } + + float4 texel = image.Sample(textureSampler, uv); + + if ( Antialias_Edges ) { + // Anti-alias edges of texture + texel.a = aastepEdgeAlpha(texel.a, p, Top_Left, Top_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Top_Right, Bottom_Right); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Right, Bottom_Left); + texel.a = aastepEdgeAlpha(texel.a, p, Bottom_Left, Top_Left); + } + return texel; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' } switch -regex ($myVerb) { Get { @@ -35114,26 +26960,22 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGBAPercentShader { +function Get-OBSCrtCurvatureShader { -[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] +[Alias('Set-OBSCrtCurvatureShader','Add-OBSCrtCurvatureShader')] param( -# Set the RedPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('RedPercent')] -[Single] -$RedPercent, -# Set the GreenPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('GreenPercent')] -[Single] -$GreenPercent, -# Set the BluePercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('BluePercent')] +# Set the strength of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('strength')] [Single] -$BluePercent, -# Set the AlphaPercent of OBSRGBAPercentShader -[ComponentModel.DefaultBindingProperty('AlphaPercent')] +$Strength, +# Set the border of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('border')] +[String] +$Border, +# Set the feathering of OBSCrtCurvatureShader +[ComponentModel.DefaultBindingProperty('feathering')] [Single] -$AlphaPercent, +$Feathering, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35164,58 +27006,48 @@ $UseShaderTime process { -$shaderName = 'RGBA_Percent' -$ShaderNoun = 'OBSRGBAPercentShader' +$shaderName = 'crt-curvature' +$ShaderNoun = 'OBSCrtCurvatureShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple RGBA Percent Shader -// Allows Red, Green, or Blue to be adjusted between 0-200% -// Allows Alpha to be adjusted between 0/100% -uniform float RedPercent< - string label = "Red percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; - -uniform float GreenPercent< - string label = "Green percentage"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 200; - float step = 1.0; -> = 100; +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 200.; + float step = 0.01; +> = 33.33; -uniform float BluePercent< - string label = "Blue percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200; - float step = 1.0; -> = 100.0; +uniform float4 border< + string label = "Border Color"; +> = {0., 0., 0., 1.}; +uniform float feathering< + string label = "Feathering"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.33; -uniform float AlphaPercent< - string label = "Alpha percentage"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; float4 mainImage(VertData v_in) : TARGET { - float2 pos = v_in.uv; - - float4 imageColors = image.Sample(textureSampler, v_in.uv); - float4 adjustedColor = float4( - imageColors.r * (RedPercent * 0.01), - imageColors.g * (GreenPercent * 0.01), - imageColors.b * (BluePercent * 0.01), - imageColors.a * (AlphaPercent * 0.01) - ); - return adjustedColor; + float2 cc = v_in.uv - float2(0.5, 0.5); + float dist = dot(cc, cc) * strength / 100.0; + float2 bent = v_in.uv + cc * (1.0 + dist) * dist; + if ((bent.x <= 0.0 || bent.x >= 1.0) || (bent.y <= 0.0 || bent.y >= 1.0)) { + return border; + } + if (feathering >= .01) { + float2 borderArea = float2(0.5, 0.5) * feathering / 100.0; + float2 borderDistance = (float2(0.5, 0.5) - abs(bent - float2(0.5, 0.5))) / float2(0.5, 0.5); + borderDistance = (min(borderDistance - float2(0.5, 0.5) * feathering / 100.0, 0) + borderArea) / borderArea; + float borderFade = sin(borderDistance.x * 1.570796326) * sin(borderDistance.y * 1.570796326); + return lerp(border, image.Sample(textureSampler, bent), borderFade); + } + + return image.Sample(textureSampler, bent); } ' @@ -35315,54 +27147,37 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbColorWheelShader { +function Get-OBSCubeRotatingShader { -[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] +[Alias('Set-OBSCubeRotatingShader','Add-OBSCubeRotatingShader')] param( -# Set the speed of OBSRgbColorWheelShader +# Set the images of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('images')] +[Int32] +$Images, +# Set the speed of OBSCubeRotatingShader [ComponentModel.DefaultBindingProperty('speed')] [Single] $Speed, -# Set the color_depth of OBSRgbColorWheelShader -[Alias('color_depth')] -[ComponentModel.DefaultBindingProperty('color_depth')] +# Set the shadow of OBSCubeRotatingShader +[ComponentModel.DefaultBindingProperty('shadow')] [Single] -$ColorDepth, -# Set the Apply_To_Image of OBSRgbColorWheelShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSRgbColorWheelShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSRgbColorWheelShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +$Shadow, +# Set the other_image1 of OBSCubeRotatingShader +[Alias('other_image1')] +[ComponentModel.DefaultBindingProperty('other_image1')] [String] -$ColorToReplace, -# Set the Alpha_Percentage of OBSRgbColorWheelShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the center_width_percentage of OBSRgbColorWheelShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRgbColorWheelShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, +$OtherImage1, +# Set the other_image2 of OBSCubeRotatingShader +[Alias('other_image2')] +[ComponentModel.DefaultBindingProperty('other_image2')] +[String] +$OtherImage2, +# Set the other_image3 of OBSCubeRotatingShader +[Alias('other_image3')] +[ComponentModel.DefaultBindingProperty('other_image3')] +[String] +$OtherImage3, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35393,101 +27208,84 @@ $UseShaderTime process { -$shaderName = 'rgb_color_wheel' -$ShaderNoun = 'OBSRgbColorWheelShader' +$shaderName = 'cube_rotating' +$ShaderNoun = 'OBSCubeRotatingShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 -// https://github.com/Oncorporation/obs-shaderfilter -//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform int images< + string label = "Images"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "1"; + int option_1_value = 2; + string option_1_label = "2"; + int option_2_value = 4; + string option_2_label = "4"; +> = 1; + uniform float speed< string label = "Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 15.0; - float step = 0.1; -> = 2.0; -uniform float color_depth< - string label = "Color Depth"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 2.10; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; -uniform float Alpha_Percentage< - string label = "Alpha Percentage"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; + +uniform float shadow< + string label = "Shadow"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; - float step = 0.1; -> = 100; // -uniform int center_width_percentage< - string label = "center width percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; - -float3 hsv2rgb(float3 c) -{ - float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -} + float maximum = 2.5; + float step = 0.001; +> = 1.0; -float mod(float x, float y) -{ - return x - y * floor(x / y); -} +uniform texture2d other_image1; +uniform texture2d other_image2; +uniform texture2d other_image3; +#define PI 3.14159265359 float4 mainImage(VertData v_in) : TARGET { - const float PI = 3.14159265f;//acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - float2 st = v_in.uv* uv_scale; - float2 toCenter = center_pixel_coordinates - st ; - float r = length(toCenter) * color_depth; - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(speed ,18)) / 18; - - rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); - - float4 color; - float4 original_color; - if (Apply_To_Image) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; - if (Replace_Image_Color) - color = float4(luma, luma, luma, luma); - rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); - + float t = elapsed_time * speed; + float4 c = float4(0,0,0,0); + for(float side = 0.0; side<4.0; side += 1.0){ + float left = cos(t+((side*0.5-0.25)*PI))/2+0.5; + float right = cos(t+((side*0.5+0.25)*PI))/2+0.5; + if(left < right){ + float2 uv; + uv.x = (v_in.uv.x-left)/(right-left); + float left_size = 1.0 +sin(t+((side*0.5-0.25)*PI))/2+0.5; + float right_size = 1.0 + sin(t+((side*0.5+0.25)*PI))/2+0.5; + float size = (uv.x * right_size) + ((1.0-uv.x) * left_size); + uv.y = (v_in.uv.y-0.5)*size+0.5; + float4 sample = float4(0,0,0,0); + if(images <= 1 || side == 0.0){ + sample = image.Sample(textureSampler, uv); + }else if(images == 2){ + if(side == 1.0 || side == 3.0){ + sample = other_image1.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + }else if(images == 4){ + if(side == 1.0){ + sample = other_image1.Sample(textureSampler, uv); + }else if(side == 2.0){ + sample = other_image2.Sample(textureSampler, uv); + }else if(side == 3.0){ + sample = other_image3.Sample(textureSampler, uv); + }else{ + sample = image.Sample(textureSampler, uv); + } + } + if(sample.a > 0.0){ + c += float4(sample.rgb*(1.0-abs((left+right)/2.0-0.5)*shadow),sample.a); + } + } } - if (Apply_To_Specific_Color) - { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); - } - - return rgba; + return c; } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -35585,34 +27383,23 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbSplitShader { +function Get-OBSCurveShader { -[Alias('Set-OBSRgbSplitShader','Add-OBSRgbSplitShader')] +[Alias('Set-OBSCurveShader','Add-OBSCurveShader')] param( -# Set the redx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redx')] -[Single] -$Redx, -# Set the redy of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('redy')] -[Single] -$Redy, -# Set the greenx of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greenx')] -[Single] -$Greenx, -# Set the greeny of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('greeny')] -[Single] -$Greeny, -# Set the bluex of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluex')] +# Set the strength of OBSCurveShader +[ComponentModel.DefaultBindingProperty('strength')] [Single] -$Bluex, -# Set the bluey of OBSRgbSplitShader -[ComponentModel.DefaultBindingProperty('bluey')] +$Strength, +# Set the scale of OBSCurveShader +[ComponentModel.DefaultBindingProperty('scale')] [Single] -$Bluey, +$Scale, +# Set the curve_color of OBSCurveShader +[Alias('curve_color')] +[ComponentModel.DefaultBindingProperty('curve_color')] +[String] +$CurveColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35643,64 +27430,50 @@ $UseShaderTime process { -$shaderName = 'rgb_split' -$ShaderNoun = 'OBSRgbSplitShader' +$shaderName = 'curve' +$ShaderNoun = 'OBSCurveShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float redx< - string label = "Red X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 2.00; -uniform float redy< - string label = "Red Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greenx< - string label = "Green X"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float greeny< - string label = "Green Y"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; -uniform float bluex< - string label = "Blue X"; +#define PI 3.14159265359 + +uniform float strength< + string label = "Strength"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = -2.00; -uniform float bluey< - string label = "Blue Y"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; + +uniform float scale< + string label = "Scale"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.01; -> = 0.00; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float4 curve_color = {0,0,0,0}; float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - if(redx != 0.0 || redy != 0.0) - c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; - if(greenx != 0.0 || greeny != 0.0) - c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; - if(bluex != 0.0 || bluey != 0.0) - c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; - return c; + float2 uv = v_in.uv; + const float ydiff = 1.0; + uv -= float2(0.5,ydiff); + uv.x *= ( uv_size.x / uv_size.y); + uv /= scale; + if(strength > 0.0){ + float d = tan((1.0-strength)*PI/2.0); + float r = length(float2(0.5*(uv_size.x / uv_size.y), d)); + float2 center = float2(0.0,(1.0-ydiff)+d); + float pd = distance(uv, center); + if(pd < r) + return curve_color; + float angle = atan2(center.x-uv.x,center.y-uv.y); + uv = float2(uv.x + sin(angle)*(pd-r),(1.0-ydiff)-(pd-r)); + } + uv.x /= ( uv_size.x / uv_size.y); + uv += float2(0.5,ydiff); + return image.Sample(textureSampler,uv); } ' } @@ -35799,38 +27572,55 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRgbvisibilityShader { +function Get-OBSCutRectPerCornerShader { -[Alias('Set-OBSRgbvisibilityShader','Add-OBSRgbvisibilityShader')] +[Alias('Set-OBSCutRectPerCornerShader','Add-OBSCutRectPerCornerShader')] param( -# Set the Red of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Red')] -[Single] -$Red, -# Set the Green of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Green')] -[Single] -$Green, -# Set the Blue of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('Blue')] -[Single] -$Blue, -# Set the RedVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('RedVisibility')] -[Single] -$RedVisibility, -# Set the GreenVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('GreenVisibility')] -[Single] -$GreenVisibility, -# Set the BlueVisibility of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('BlueVisibility')] -[Single] -$BlueVisibility, -# Set the notes of OBSRgbvisibilityShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the corner_tl of OBSCutRectPerCornerShader +[Alias('corner_tl')] +[ComponentModel.DefaultBindingProperty('corner_tl')] +[Int32] +$CornerTl, +# Set the corner_tr of OBSCutRectPerCornerShader +[Alias('corner_tr')] +[ComponentModel.DefaultBindingProperty('corner_tr')] +[Int32] +$CornerTr, +# Set the corner_br of OBSCutRectPerCornerShader +[Alias('corner_br')] +[ComponentModel.DefaultBindingProperty('corner_br')] +[Int32] +$CornerBr, +# Set the corner_bl of OBSCutRectPerCornerShader +[Alias('corner_bl')] +[ComponentModel.DefaultBindingProperty('corner_bl')] +[Int32] +$CornerBl, +# Set the border_thickness of OBSCutRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSCutRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] [String] -$Notes, +$BorderColor, +# Set the border_alpha_start of OBSCutRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSCutRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSCutRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -35861,74 +27651,186 @@ $UseShaderTime process { -$shaderName = 'rgbvisibility' -$ShaderNoun = 'OBSRgbvisibilityShader' +$shaderName = 'cut_rect_per_corner' +$ShaderNoun = 'OBSCutRectPerCornerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGB visibility separation filter, created by EposVox - -uniform float Red< - string label = "Red"; +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_tl< + string label = "Corner top left"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; - -uniform float Green< - string label = "Green"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_tr< + string label = "Corner top right"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; - -uniform float Blue< - string label = "Blue"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_br< + string label = "Corner bottom right"; string widget_type = "slider"; - float minimum = 0.1; - float maximum = 10.0; - float step = 0.01; -> = 2.2; - -uniform float RedVisibility< - string label = "Red Visibility"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int corner_bl< + string label = "Corner bottom left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border aplha start"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.01; > = 1.0; - -uniform float GreenVisibility< - string label = "Green Visibility"; +uniform float border_alpha_end< + string label = "Border aplha start"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.01; -> = 1.0; - -uniform float BlueVisibility< - string label = "Blue Visibility"; +> = 0.0; +uniform float alpha_cut_off< + string label = "Alpha cut off"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.01; -> = 1.0; - -uniform string notes< - string widget_type = "info"; -> = "Modify Colors to correct for gamma, use equal values for general correction."; +> = 0.5; float4 mainImage(VertData v_in) : TARGET { - float4 c = image.Sample(textureSampler, v_in.uv); - float redChannel = pow(c.r, Red) * RedVisibility; - float greenChannel = pow(c.g, Green) * GreenVisibility; - float blueChannel = pow(c.b, Blue) * BlueVisibility; + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + int corner_top = corner_tl>corner_tr?corner_tl:corner_tr; + int corner_right = corner_tr>corner_br?corner_tr:corner_br; + int corner_bottom = corner_bl>corner_br?corner_bl:corner_br; + int corner_left = corner_tl>corner_bl?corner_tl:corner_bl; - return float4(redChannel, greenChannel, blueChannel, c.a); + if(image.Sample(textureSampler, v_in.uv + float2(corner_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = closedEdgeXabs+closedEdgeYabs; + if(d>corner_radius){ + if(d-corner_radius <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((d-corner_radius)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + return float4(0.0,0.0,0.0,0.0); } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36026,22 +27928,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRGSSAAShader { +function Get-OBSCylinderShader { -[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] +[Alias('Set-OBSCylinderShader','Add-OBSCylinderShader')] param( -# Set the ColorSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('ColorSigma')] +# Set the cylinder_factor of OBSCylinderShader +[Alias('cylinder_factor')] +[ComponentModel.DefaultBindingProperty('cylinder_factor')] [Single] -$ColorSigma, -# Set the SpatialSigma of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('SpatialSigma')] +$CylinderFactor, +# Set the background_cut of OBSCylinderShader +[Alias('background_cut')] +[ComponentModel.DefaultBindingProperty('background_cut')] [Single] -$SpatialSigma, -# Set the notes of OBSRGSSAAShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$BackgroundCut, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36072,156 +27972,59 @@ $UseShaderTime process { -$shaderName = 'RGSSAA' -$ShaderNoun = 'OBSRGSSAAShader' +$shaderName = 'cylinder' +$ShaderNoun = 'OBSCylinderShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 -// https://github.com/exeldro/obs-shaderfilter/tree/master -// Using edge detection shader as a base, created by Hallatore -// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 - -uniform float ColorSigma< - string label = "Color Sigma"; +uniform float cylinder_factor< + string label = "Cylinder factor"; string widget_type = "slider"; - float minimum = 0.1; + float minimum = -1.0; float maximum = 1.0; - float step = 0.1; -> = 1.0; - -uniform float SpatialSigma< - string label = "Spatial Sigma"; + float step = 0.001; +> = 0.2; +uniform float background_cut< + string label = "Background cut"; string widget_type = "slider"; - float minimum = 0.1; + float minimum = 0.0; float maximum = 1.0; - float step = 0.1; -> = 1.0; - -uniform string notes< - string widget_type = "info"; -> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." - -float Luminance(float3 rgb) -{ - return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; -} + float step = 0.001; +> = 0.1; float4 mainImage(VertData v_in) : TARGET { - float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; - float2 SceneUV = v_in.uv; - float2 TexelScale = 1.0f/uv_size; - - const float SQRT2 = 1.4142135624; - const float PI = 3.141592654; - const float angle = PI / 8.0; - const float cs = cos(angle); - const float sn = sin(angle); - - // Rotated grid samples - float3 C1 = - image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; - float3 C2 = - image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; - float3 C3 = - image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; - float3 C4 = - image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; - float3 C5 = - image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; - float3 C6 = - image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; - float3 C7 = - image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; - float3 C8 = - image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; - - // Luminance edge detection - float A0 = Luminance(SceneColor); - float CL1 = Luminance(C1); - float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); - float CL2 = Luminance(C2); - float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); - float CL3 = Luminance(C3); - float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); - float CL4 = Luminance(C4); - float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); - float CL5 = Luminance(C5); - float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); - float CL6 = Luminance(C6); - float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); - float CL7 = Luminance(C7); - float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); - float CL8 = Luminance(C8); - float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); - float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); - - // Calculate distance-based weights - float2 Dist1 = float2(cs, -sn); - float2 Dist2 = float2(-cs, -sn); - float2 Dist3 = float2(-sn, cs); - float2 Dist4 = float2(sn, cs); - float2 Dist5 = float2(cs * SQRT2, 0); - float2 Dist6 = float2(0, sn * SQRT2); - float2 Dist7 = float2(-cs * SQRT2, 0); - float2 Dist8 = float2(0, -sn * SQRT2); - float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); - float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); - float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); - float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); - float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); - float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); - float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); - float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); - - // Color weights - float3 ColorDiff1 = SceneColor.rgb - C1; - float3 ColorDiff2 = SceneColor.rgb - C2; - float3 ColorDiff3 = SceneColor.rgb - C3; - float3 ColorDiff4 = SceneColor.rgb - C4; - float3 ColorDiff5 = SceneColor.rgb - C5; - float3 ColorDiff6 = SceneColor.rgb - C6; - float3 ColorDiff7 = SceneColor.rgb - C7; - float3 ColorDiff8 = SceneColor.rgb - C8; - - float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); - float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); - float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); - float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); - float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); - float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); - float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); - float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); - - // Mixing weights - float W1 = SW1 * CW1; - float W2 = SW2 * CW2; - float W3 = SW3 * CW3; - float W4 = SW4 * CW4; - float W5 = SW5 * CW5; - float W6 = SW6 * CW6; - float W7 = SW7 * CW7; - float W8 = SW8 * CW8; - float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; - - // Weighted color - float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + - C7 * W7 + C8 * W8) / - max(TotalWeight, 0.0001); - - // Blend it - float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); - float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); - float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); - float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); - float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); - float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); - float HDREdge = max(A0Max, A0Max2); - float EdgeMask = saturate(1.0f - HDREdge); + float2 uv = v_in.uv; + uv.x -= 0.5; + float bend = sqrt(1.0 - uv.x*uv.x*4); + uv.y = uv.y/(1.0 - cylinder_factor)-bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + uv.x += 0.5; + float4 front_color = image.Sample(textureSampler, uv); + front_color.rgb *= bend/2+0.5; + if(front_color.a >= 1.0) + return front_color; + + uv = v_in.uv; + uv.x -= 0.5; + if(abs(uv.x) < background_cut) + return front_color; + uv.y = uv.y/(1.0 - cylinder_factor)+bend*cylinder_factor; + uv.y-=cylinder_factor/2; + uv.x /= 2; + if(uv.x > 0){ + uv.x = 1.0 - uv.x; + }else{ + uv.x = 0 - uv.x; + } - return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); + float4 back_color = image.Sample(textureSampler, uv); + back_color.rgb *= 0.5-bend/2; + front_color.rgb *= front_color.a; + front_color.rgb += back_color.rgb * (1.0 - front_color.a) * back_color.a; + front_color.a = back_color.a * (1.0 - front_color.a) + front_color.a; + return front_color; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36319,35 +28122,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRippleShader { +function Get-OBSDarkenShader { -[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] +[Alias('Set-OBSDarkenShader','Add-OBSDarkenShader')] param( -# Set the distance_factor of OBSRippleShader -[Alias('distance_factor')] -[ComponentModel.DefaultBindingProperty('distance_factor')] -[Single] -$DistanceFactor, -# Set the time_factor of OBSRippleShader -[Alias('time_factor')] -[ComponentModel.DefaultBindingProperty('time_factor')] -[Single] -$TimeFactor, -# Set the power_factor of OBSRippleShader -[Alias('power_factor')] -[ComponentModel.DefaultBindingProperty('power_factor')] -[Single] -$PowerFactor, -# Set the center_pos_x of OBSRippleShader -[Alias('center_pos_x')] -[ComponentModel.DefaultBindingProperty('center_pos_x')] +# Set the Opacity_Percentage of OBSDarkenShader +[Alias('Opacity_Percentage')] +[ComponentModel.DefaultBindingProperty('Opacity_Percentage')] [Single] -$CenterPosX, -# Set the center_pos_y of OBSRippleShader -[Alias('center_pos_y')] -[ComponentModel.DefaultBindingProperty('center_pos_y')] +$OpacityPercentage, +# Set the Fill_Percentage of OBSDarkenShader +[Alias('Fill_Percentage')] +[ComponentModel.DefaultBindingProperty('Fill_Percentage')] [Single] -$CenterPosY, +$FillPercentage, +# Set the Notes of OBSDarkenShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36378,54 +28170,24 @@ $UseShaderTime process { -$shaderName = 'ripple' -$ShaderNoun = 'OBSRippleShader' +$shaderName = 'darken' +$ShaderNoun = 'OBSDarkenShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform float distance_factor< - string label = "distance factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 12.0; -uniform float time_factor< - string label = "time factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform float power_factor< - string label = "power factor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 3.0; -uniform float center_pos_x< - string label = "center pos x"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float center_pos_y< - string label = "center pos y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; +uniform float Opacity_Percentage = 100.0; +uniform float Fill_Percentage = 100.0; +uniform string Notes = "Simulates a photo editing darken layer blending mode. Fill percentage is the interior alpha and Opacity is the layer alpha."; float4 mainImage(VertData v_in) : TARGET { - float2 cPos = (v_in.uv * 2 ) -1; - float2 center_pos = float2(center_pos_x, center_pos_y); - float cLength = distance(cPos, center_pos); - float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; - return image.Sample(textureSampler, uv); + float4 other = float4(1.0, 1.0, 1.0, 1.0); + float4 base = image.Sample(textureSampler, v_in.uv); + float luminance = dot(base.rgb, float3(0.299, 0.587, 0.114)); + float4 gray = float4(luminance,luminance,luminance, 1.0); + + return min(base,other); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36523,38 +28285,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatingSourceShader { +function Get-OBSDeadPixelFixerShader { -[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] +[Alias('Set-OBSDeadPixelFixerShader','Add-OBSDeadPixelFixerShader')] param( -# Set the spin_speed of OBSRotatingSourceShader -[Alias('spin_speed')] -[ComponentModel.DefaultBindingProperty('spin_speed')] -[Single] -$SpinSpeed, -# Set the rotation of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('rotation')] -[Single] -$Rotation, -# Set the zoomin of OBSRotatingSourceShader -[ComponentModel.DefaultBindingProperty('zoomin')] -[Single] -$Zoomin, -# Set the keep_aspectratio of OBSRotatingSourceShader -[Alias('keep_aspectratio')] -[ComponentModel.DefaultBindingProperty('keep_aspectratio')] -[Management.Automation.SwitchParameter] -$KeepAspectratio, -# Set the x_center of OBSRotatingSourceShader -[Alias('x_center')] -[ComponentModel.DefaultBindingProperty('x_center')] -[Single] -$XCenter, -# Set the y_center of OBSRotatingSourceShader -[Alias('y_center')] -[ComponentModel.DefaultBindingProperty('y_center')] -[Single] -$YCenter, +# Set the Dead_Pixel_X of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_X')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_X')] +[Int32] +$DeadPixelX, +# Set the Dead_Pixel_Y of OBSDeadPixelFixerShader +[Alias('Dead_Pixel_Y')] +[ComponentModel.DefaultBindingProperty('Dead_Pixel_Y')] +[Int32] +$DeadPixelY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36585,86 +28329,65 @@ $UseShaderTime process { -$shaderName = 'rotating-source' -$ShaderNoun = 'OBSRotatingSourceShader' +$shaderName = 'dead-pixel-fixer' +$ShaderNoun = 'OBSDeadPixelFixerShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//spin speed higher the slower -uniform float spin_speed< - string label = "Spin Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float rotation< - string label = "Rotation"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.1; -> = 0.0; -uniform float zoomin< - string label = "Zoom"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform bool keep_aspectratio = true; -uniform float x_center< - string label = "Center x"; +// Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixel located at the user configured offset will have its color overridden by taking the average of the +// color of the 8 pixels immediately surrounding it, effectively hiding the dead pixel. +// +// Changelog: +// 0.01 - Initial release + +uniform int Dead_Pixel_X< + string label = "Dead Pixel X"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform float y_center< - string label = "Center y"; + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; +uniform int Dead_Pixel_Y< + string label = "Dead Pixel Y"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; + int minimum = 0; + int maximum = 2000; + int step = 1; +> = 0; +float3 blur_dead_pixel(in float2 pos) +{ + float3 color; + color = image.Sample(textureSampler, (pos + float2(-1.0, -0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(0.5, -1.0))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(1.0, 0.5))/uv_size).rgb; + color += image.Sample(textureSampler, (pos + float2(-0.5, 1.0))/uv_size).rgb; + return color * 0.25; +} -//main fragment code -//from lioran to nutella with love -float4 mainImage(VertData v_in) : TARGET +float4 mainImage( VertData v_in ) : TARGET { - float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; - float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; - //get position on of the texture and focus on the middle - float i_rotation; - if (spin_speed == 0){ - //turn angle number into pi number - i_rotation = rotation/57.295779513; - }else{ - //use elapsed time for spinning if spin speed is not 0 - i_rotation = elapsed_time * spin_speed; - } - float2 i_point; - i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); - i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); - - //get the angle from center , returns pi number - float i_dir = atan(i_point.y/i_point.x); - if(i_point.x < 0.0){ - i_dir += 3.14159265359; - } - - //get the distance from the centers - float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); - //multiple distance by the zoomin value - i_distance *= zoomin; - - //shift the texture position based on angle and distance from the middle - i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; - i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; - - //draw normally from new point - return image.Sample(textureSampler, i_point); + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; + float2 dp_pos = clamp( float2(Dead_Pixel_X, Dead_Pixel_Y), float2(0.0,0.0), uv_size - 1); + float4 obstex = image.Sample(textureSampler, uv); + float3 color; + + if(floor(pos.x) == floor(dp_pos.x) && floor(pos.y) == floor(dp_pos.y) ) { + color = blur_dead_pixel(pos); + } else { + color.rgb = obstex.rgb; + } + return float4( color, obstex.a); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -36762,102 +28485,114 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRotatoeShader { +function Get-OBSDensitySatHueShader { -[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] +[Alias('Set-OBSDensitySatHueShader','Add-OBSDensitySatHueShader')] param( -# Set the ViewProj of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] -[Single[][]] -$ViewProj, -# Set the image of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('image')] +# Set the notes of OBSDensitySatHueShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$Image, -# Set the elapsed_time of OBSRotatoeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] +$Notes, +# Set the density_r of OBSDensitySatHueShader +[Alias('density_r')] +[ComponentModel.DefaultBindingProperty('density_r')] [Single] -$ElapsedTime, -# Set the uv_offset of OBSRotatoeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSRotatoeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSRotatoeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSRotatoeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] +$DensityR, +# Set the saturation_r of OBSDensitySatHueShader +[Alias('saturation_r')] +[ComponentModel.DefaultBindingProperty('saturation_r')] [Single] -$RandF, -# Set the uv_size of OBSRotatoeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the speed_percent of OBSRotatoeShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the Axis_X of OBSRotatoeShader -[Alias('Axis_X')] -[ComponentModel.DefaultBindingProperty('Axis_X')] +$SaturationR, +# Set the hueShift_r of OBSDensitySatHueShader +[Alias('hueShift_r')] +[ComponentModel.DefaultBindingProperty('hueShift_r')] [Single] -$AxisX, -# Set the Axis_Y of OBSRotatoeShader -[Alias('Axis_Y')] -[ComponentModel.DefaultBindingProperty('Axis_Y')] +$HueShiftR, +# Set the density_y of OBSDensitySatHueShader +[Alias('density_y')] +[ComponentModel.DefaultBindingProperty('density_y')] [Single] -$AxisY, -# Set the Axis_Z of OBSRotatoeShader -[Alias('Axis_Z')] -[ComponentModel.DefaultBindingProperty('Axis_Z')] +$DensityY, +# Set the saturation_y of OBSDensitySatHueShader +[Alias('saturation_y')] +[ComponentModel.DefaultBindingProperty('saturation_y')] [Single] -$AxisZ, -# Set the Angle_Degrees of OBSRotatoeShader -[Alias('Angle_Degrees')] -[ComponentModel.DefaultBindingProperty('Angle_Degrees')] +$SaturationY, +# Set the hueShift_y of OBSDensitySatHueShader +[Alias('hueShift_y')] +[ComponentModel.DefaultBindingProperty('hueShift_y')] [Single] -$AngleDegrees, -# Set the Rotate_Transform of OBSRotatoeShader -[Alias('Rotate_Transform')] -[ComponentModel.DefaultBindingProperty('Rotate_Transform')] -[Management.Automation.SwitchParameter] -$RotateTransform, -# Set the Rotate_Pixels of OBSRotatoeShader -[Alias('Rotate_Pixels')] -[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] -[Management.Automation.SwitchParameter] -$RotatePixels, -# Set the Rotate_Colors of OBSRotatoeShader -[Alias('Rotate_Colors')] -[ComponentModel.DefaultBindingProperty('Rotate_Colors')] -[Management.Automation.SwitchParameter] -$RotateColors, -# Set the center_width_percentage of OBSRotatoeShader -[Alias('center_width_percentage')] -[ComponentModel.DefaultBindingProperty('center_width_percentage')] -[Int32] -$CenterWidthPercentage, -# Set the center_height_percentage of OBSRotatoeShader -[Alias('center_height_percentage')] -[ComponentModel.DefaultBindingProperty('center_height_percentage')] -[Int32] -$CenterHeightPercentage, -# Set the notes of OBSRotatoeShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$HueShiftY, +# Set the density_g of OBSDensitySatHueShader +[Alias('density_g')] +[ComponentModel.DefaultBindingProperty('density_g')] +[Single] +$DensityG, +# Set the saturation_g of OBSDensitySatHueShader +[Alias('saturation_g')] +[ComponentModel.DefaultBindingProperty('saturation_g')] +[Single] +$SaturationG, +# Set the hueShift_g of OBSDensitySatHueShader +[Alias('hueShift_g')] +[ComponentModel.DefaultBindingProperty('hueShift_g')] +[Single] +$HueShiftG, +# Set the density_c of OBSDensitySatHueShader +[Alias('density_c')] +[ComponentModel.DefaultBindingProperty('density_c')] +[Single] +$DensityC, +# Set the saturation_c of OBSDensitySatHueShader +[Alias('saturation_c')] +[ComponentModel.DefaultBindingProperty('saturation_c')] +[Single] +$SaturationC, +# Set the hueShift_c of OBSDensitySatHueShader +[Alias('hueShift_c')] +[ComponentModel.DefaultBindingProperty('hueShift_c')] +[Single] +$HueShiftC, +# Set the density_b of OBSDensitySatHueShader +[Alias('density_b')] +[ComponentModel.DefaultBindingProperty('density_b')] +[Single] +$DensityB, +# Set the saturation_b of OBSDensitySatHueShader +[Alias('saturation_b')] +[ComponentModel.DefaultBindingProperty('saturation_b')] +[Single] +$SaturationB, +# Set the hueShift_b of OBSDensitySatHueShader +[Alias('hueShift_b')] +[ComponentModel.DefaultBindingProperty('hueShift_b')] +[Single] +$HueShiftB, +# Set the density_m of OBSDensitySatHueShader +[Alias('density_m')] +[ComponentModel.DefaultBindingProperty('density_m')] +[Single] +$DensityM, +# Set the saturation_m of OBSDensitySatHueShader +[Alias('saturation_m')] +[ComponentModel.DefaultBindingProperty('saturation_m')] +[Single] +$SaturationM, +# Set the hueShift_m of OBSDensitySatHueShader +[Alias('hueShift_m')] +[ComponentModel.DefaultBindingProperty('hueShift_m')] +[Single] +$HueShiftM, +# Set the global_density of OBSDensitySatHueShader +[Alias('global_density')] +[ComponentModel.DefaultBindingProperty('global_density')] +[Single] +$GlobalDensity, +# Set the global_saturation of OBSDensitySatHueShader +[Alias('global_saturation')] +[ComponentModel.DefaultBindingProperty('global_saturation')] +[Single] +$GlobalSaturation, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -36888,157 +28623,259 @@ $UseShaderTime process { -$shaderName = 'rotatoe' -$ShaderNoun = 'OBSRotatoeShader' +$shaderName = 'density_sat_hue' +$ShaderNoun = 'OBSDensitySatHueShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 -//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow -uniform float4x4 ViewProj; -uniform texture2d image; +// Density-Saturation-Hue Shader for OBS Shaderfilter +// Modified by CameraTim for use with obs-shaderfilter 12/2024 v.12 -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; +uniform string notes< + string widget_type = "info"; +> = "Density adjustment shader: Density reduces luminance, while saturation is subtractively increased to avoid greyish colors."; -uniform int speed_percent< - string label = "speed percentage"; - string widget_type = "slider"; - int minimum = -100; - int maximum = 100; - int step = 1; -> = 50; // -uniform float Axis_X< - string label = "Axis X"; +uniform float density_r < + string label = "Red Density"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.1; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; > = 0.0; -uniform float Axis_Y< - string label = "Axis Y"; + +uniform float saturation_r < + string label = "Red Sat"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; > = 0.0; -uniform float Axis_Z< - string label = "Axis Z"; + +uniform float hueShift_r < + string label = "Red Hue Shift"; string widget_type = "slider"; - float minimum = -2.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; -uniform float Angle_Degrees< - string label = "Angle Degrees"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float density_y < + string label = "Yellow Density"; string widget_type = "slider"; - float minimum = -180.0; - float maximum = 180.0; - float step = 0.01; -> = 45.0; -uniform bool Rotate_Transform = true; -uniform bool Rotate_Pixels = false; -uniform bool Rotate_Colors = false; -uniform int center_width_percentage< - string label = "center width percentage"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float saturation_y < + string label = "Yellow Sat"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height_percentage< - string label = "center height percentage"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform float hueShift_y < + string label = "Yellow Hue Shift"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -uniform string notes< - string widget_type = "info"; -> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; +uniform float density_g < + string label = "Green Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; +uniform float saturation_g < + string label = "Green Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +uniform float hueShift_g < + string label = "Green Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float3x3 rotAxis(float3 axis, float a) { - float s=sin(a); - float c=cos(a); - float oc=1.0-c; +uniform float density_c < + string label = "Cyan Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - float3 as=axis*s; +uniform float saturation_c < + string label = "Cyan Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); - float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); - return p*oc+q; -} +uniform float hueShift_c < + string label = "Cyan Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -VertData mainTransform(VertData v_in) -{ - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); +uniform float density_b < + string label = "Blue Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; +uniform float saturation_b < + string label = "Blue Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // use matrix to transform rotation - if (Rotate_Transform) - vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; +uniform float hueShift_b < + string label = "Blue Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - vert_out.uv = v_in.uv * uv_scale + uv_offset; +uniform float density_m < + string label = "Magenta Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - return vert_out; -} +uniform float saturation_m < + string label = "Magenta Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; -float4 mainImage(VertData v_in) : TARGET -{ - float4 rgba = image.Sample(textureSampler, v_in.uv); - - float speed = speed_percent * 0.01; - // circular easing variable - float PI = 3.1415926535897932384626433832795; //acos(-1); - float PI180th = 0.0174532925; //PI divided by 180 - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = sin(elapsed_time * speed); - float angle_degrees = PI180th * Angle_Degrees; +uniform float hueShift_m < + string label = "Magenta Hue Shift"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float global_density < + string label = "Global Density"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - // use matrix to transform pixels - if (Rotate_Pixels) - { - float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); - rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); - } - if (Rotate_Colors) - rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; +uniform float global_saturation < + string label = "Global Sat"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - return rgba; +// Tetrahedral interpolation based on the ordering of the input color channels +float3 applyAdjustments(float3 p) { + // Pre-calculate the flipped density values for each channel: + float d_r = -(density_r + global_density); + float d_y = -(density_y + global_density); + float d_g = -(density_g + global_density); + float d_c = -(density_c + global_density); + float d_b = -(density_b + global_density); + float d_m = -(density_m + global_density); + + // Compute the color vectors for each hue region using the flipped densities: + float3 red = float3( + 1.0 + d_r, + d_r - (saturation_r + global_saturation), + d_r + hueShift_r - (saturation_r + global_saturation) + ); + + float3 yellow = float3( + 1.0 + hueShift_y + d_y, + 1.0 + d_y, + d_y - (saturation_y + global_saturation) + ); + + float3 green = float3( + d_g - (saturation_g + global_saturation), + 1.0 + d_g, + d_g + hueShift_g - (saturation_g + global_saturation) + ); + + float3 cyan = float3( + d_c - (saturation_c + global_saturation), + 1.0 + hueShift_c + d_c, + 1.0 + d_c + ); + + float3 blue = float3( + d_b + hueShift_b - (saturation_b + global_saturation), + d_b - (saturation_b + global_saturation), + 1.0 + d_b + ); + + float3 magenta = float3( + 1.0 + d_m, + d_m - (saturation_m + global_saturation), + 1.0 + hueShift_m + d_m + ); + + // Define the black and white endpoints: + float3 blk = float3(0.0, 0.0, 0.0); + float3 wht = float3(1.0, 1.0, 1.0); + + float3 rgb; + if (p.r > p.g) { + if (p.g > p.b) { + // p.r >= p.g >= p.b + rgb = p.r * (red - blk) + blk + p.g * (yellow - red) + p.b * (wht - yellow); + } else if (p.r > p.b) { + // p.r >= p.b > p.g + rgb = p.r * (red - blk) + blk + p.g * (wht - magenta) + p.b * (magenta - red); + } else { + // p.b >= p.r > p.g + rgb = p.r * (magenta - blue) + p.g * (wht - magenta) + p.b * (blue - blk) + blk; + } + } else { + if (p.b > p.g) { + // p.b >= p.g >= p.r + rgb = p.r * (wht - cyan) + p.g * (cyan - blue) + p.b * (blue - blk) + blk; + } else if (p.b > p.r) { + // p.g >= p.r and p.b > p.r + rgb = p.r * (wht - cyan) + p.g * (green - blk) + blk + p.b * (cyan - green); + } else { + // p.g >= p.b >= p.r + rgb = p.r * (yellow - green) + p.g * (green - blk) + blk + p.b * (wht - yellow); + } + } + return clamp(rgb, 0.0, 1.0); } -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } +float4 mainImage(VertData v_in) : TARGET { + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float3 adjustedColor = applyAdjustments(inputColor.rgb); + return float4(adjustedColor, inputColor.a); } ' @@ -37138,45 +28975,35 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRect2Shader { +function Get-OBSDiffuseTransitionShader { -[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] +[Alias('Set-OBSDiffuseTransitionShader','Add-OBSDiffuseTransitionShader')] param( -# Set the corner_radius of OBSRoundedRect2Shader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedRect2Shader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRect2Shader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +# Set the image_a of OBSDiffuseTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] [String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRect2Shader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] -[Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRect2Shader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] -[Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRect2Shader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +$ImageA, +# Set the image_b of OBSDiffuseTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSDiffuseTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] [Single] -$AlphaCutOff, -# Set the faster_scan of OBSRoundedRect2Shader -[Alias('faster_scan')] -[ComponentModel.DefaultBindingProperty('faster_scan')] +$TransitionTime, +# Set the convert_linear of OBSDiffuseTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] [Management.Automation.SwitchParameter] -$FasterScan, +$ConvertLinear, +# Set the num_samples of OBSDiffuseTransitionShader +[Alias('num_samples')] +[ComponentModel.DefaultBindingProperty('num_samples')] +[Int32] +$NumSamples, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37207,172 +29034,94 @@ $UseShaderTime process { -$shaderName = 'rounded_rect2' -$ShaderNoun = 'OBSRoundedRect2Shader' +$shaderName = 'diffuse_transition' +$ShaderNoun = 'OBSDiffuseTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; +//based on https://www.shadertoy.com/view/4cK3z1 +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; + +// Diffuse (move pixels) between two 2D images +// Demo inspired by Iterative-(de)Blending (see Figure 9 in https://arxiv.org/pdf/2305.03486.pdf) +// Note: the approach in this demo is different - rather than randomising paths we use means + +// increase for greater precision - this is O(n^2) :( +uniform int num_samples< + string label = "Number of samples (10)"; string widget_type = "slider"; - int minimum = 0; + int minimum = 2; int maximum = 100; int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "Border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 1.0; -uniform float border_alpha_end< - string label = "Border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; -uniform float alpha_cut_off< - string label = "Aplha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.5; -uniform bool faster_scan = true; +> = 10; float4 mainImage(VertData v_in) : TARGET { - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - float check_dist = float(corner_radius); - if(border_thickness > check_dist) - check_dist = border_thickness; - if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = int(-check_dist); - } - if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(check_dist); - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = int(-check_dist); - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(!faster_scan || closedEdgeX != 0){ - [loop] for(int x = 1;float(x) check_dist && border_thickness > corner_radius){ - if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ - float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(floor(cd) > corner_radius) - return float4(0.0,0.0,0.0,0.0); - if(cd > corner_radius){ - d = border_thickness + cd - corner_radius; - } else if(d > border_thickness){ - d = border_thickness; - } - }else if(d > border_thickness){ - d = border_thickness; + for (int i=0; i 0){ - if(ceil(check_dist-d) <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); - if(border_alpha_start < border_alpha_end){ - fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; - fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); - } - if(d > check_dist) - fade_color.a *= 1.0 -(d - check_dist); - return fade_color; - }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ - float4 fade_color = border_color; - float f; - if(border_thickness > (check_dist-d)) - f = border_thickness - (check_dist-d); - else - f = 1.0 -((check_dist-d) - border_thickness); - fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; - return fade_color; - } + + // only a subset of the inputs and outputs would cross our 3d coord, we can compute the ranges + // maths: https://www.desmos.com/3d/60b155c9e9 + float from_alpha = -transition_time/(1.0-transition_time); + + float2 from_start = clamp(((1.0 - uv) *from_alpha) + uv, float2(0.0,0.0), float2(1.0,1.0)); + float2 from_end = clamp(-uv * from_alpha + uv, float2(0.0,0.0), float2(1.0,1.0)); + + float to_alpha = (1.0-transition_time) / -transition_time; + + float2 to_start = clamp(-uv * to_alpha + uv, float2(0.0,0.0), float2(1.0,1.0)); + float2 to_end = clamp((1.0 - uv) * to_alpha + uv, float2(0.0,0.0), float2(1.0,1.0)); + + //all we need to do is figure out how many points from the original distribution will go through this coord on their way to the target + float3 sum = float3(0.0,0.0,0.0); + + for (int i=0; i check_dist) - pixel.a *= 1.0 - (d - check_dist); - return pixel; - } - return float4(0.0,0.0,0.0,0.0); + + //the two distributions may have a different sum so scale to blend between the two + float3 target_total = lerp(from_total, to_total, transition_time); + float3 total_multiplier = target_total / from_total; + + sum *= total_multiplier; + if (convert_linear) + sum = srgb_nonlinear_to_linear(sum); + + return float4(sum,1.0); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -37470,55 +29219,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectPerCornerShader { +function Get-OBSDigitalRainShader { -[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] +[Alias('Set-OBSDigitalRainShader','Add-OBSDigitalRainShader')] param( -# Set the corner_radius_tl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tl')] -[ComponentModel.DefaultBindingProperty('corner_radius_tl')] -[Int32] -$CornerRadiusTl, -# Set the corner_radius_tr of OBSRoundedRectPerCornerShader -[Alias('corner_radius_tr')] -[ComponentModel.DefaultBindingProperty('corner_radius_tr')] -[Int32] -$CornerRadiusTr, -# Set the corner_radius_br of OBSRoundedRectPerCornerShader -[Alias('corner_radius_br')] -[ComponentModel.DefaultBindingProperty('corner_radius_br')] -[Int32] -$CornerRadiusBr, -# Set the corner_radius_bl of OBSRoundedRectPerCornerShader -[Alias('corner_radius_bl')] -[ComponentModel.DefaultBindingProperty('corner_radius_bl')] -[Int32] -$CornerRadiusBl, -# Set the border_thickness of OBSRoundedRectPerCornerShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectPerCornerShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +# Set the font of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('font')] [String] -$BorderColor, -# Set the border_alpha_start of OBSRoundedRectPerCornerShader -[Alias('border_alpha_start')] -[ComponentModel.DefaultBindingProperty('border_alpha_start')] +$Font, +# Set the noise of OBSDigitalRainShader +[ComponentModel.DefaultBindingProperty('noise')] +[String] +$Noise, +# Set the base_color of OBSDigitalRainShader +[Alias('base_color')] +[ComponentModel.DefaultBindingProperty('base_color')] +[String] +$BaseColor, +# Set the rain_speed of OBSDigitalRainShader +[Alias('rain_speed')] +[ComponentModel.DefaultBindingProperty('rain_speed')] [Single] -$BorderAlphaStart, -# Set the border_alpha_end of OBSRoundedRectPerCornerShader -[Alias('border_alpha_end')] -[ComponentModel.DefaultBindingProperty('border_alpha_end')] +$RainSpeed, +# Set the char_speed of OBSDigitalRainShader +[Alias('char_speed')] +[ComponentModel.DefaultBindingProperty('char_speed')] [Single] -$BorderAlphaEnd, -# Set the alpha_cut_off of OBSRoundedRectPerCornerShader -[Alias('alpha_cut_off')] -[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +$CharSpeed, +# Set the glow_contrast of OBSDigitalRainShader +[Alias('glow_contrast')] +[ComponentModel.DefaultBindingProperty('glow_contrast')] [Single] -$AlphaCutOff, +$GlowContrast, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -37549,185 +29281,80 @@ $UseShaderTime process { -$shaderName = 'rounded_rect_per_corner' -$ShaderNoun = 'OBSRoundedRectPerCornerShader' +$shaderName = 'digital-rain' +$ShaderNoun = 'OBSDigitalRainShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int corner_radius_tl< - string label = "Corner radius top left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_tr< - string label = "Corner radius top right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_br< - string label = "Corner radius bottom right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_bl< - string label = "Corner radius bottom left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; +// based on https://www.shadertoy.com/view/ldccW4 by WillKirkby + + +#ifndef OPENGL +#define fract frac +#define mix lerp +#endif + +uniform texture2d font = "font.png"; +uniform texture2d noise = "noise.png"; +uniform float4 base_color = {.1, 1.0, .35, 1.0}; +uniform float rain_speed< + string label = "Rain Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; + float minimum = 0.001; + float maximum = 2.0; + float step = .001; > = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; + +uniform float char_speed< + string label = "Character Speed"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; + float maximum = 2.0; + float step = .001; +> = 1.0; + +uniform float glow_contrast< + string label = "Glow contrast"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; + float maximum = 2.5; + float step = .001; +> = 1.0; + + +float mod(float x, float y) +{ + return x - y * floor(x/y); +} + +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x/y); +} + +float text(float2 fragCoord) +{ + float2 uv = mod2(fragCoord, float2(16.0, 16.0) )/16.0; + float2 block = (fragCoord*.0625 - uv)/uv_size*16.0; + uv = uv*.8+.1; // scale the letters up a bit + block += elapsed_time*.002*char_speed; + uv += floor(noise.Sample(textureSampler, fract(block)).xy * 16.); // randomize letters + uv *= .0625; // bring back into 0-1 range + return font.Sample(textureSampler, uv).r; +} + +float3 rain(float2 fragCoord) +{ + fragCoord.x -= mod(fragCoord.x, 16.); + float offset=sin(fragCoord.x*15.); + float speed=(cos(fragCoord.x*3.)*.3+.7)*rain_speed; + + float y = fract(fragCoord.y/uv_size.y + elapsed_time*speed + offset); + return base_color.rgb / pow(y*20.0, glow_contrast); +} float4 mainImage(VertData v_in) : TARGET { - float4 pixel = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(pixel.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; - int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; - int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; - int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; - - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return pixel; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x 0 && closedEdgeY < 0){ - corner_radius = corner_radius_tr; - }else if(closedEdgeX > 0 && closedEdgeY > 0){ - corner_radius = corner_radius_br; - }else if(closedEdgeX < 0 && closedEdgeY > 0){ - corner_radius = corner_radius_bl; - } - if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ - return pixel; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeXabs > corner_radius){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - if(closedEdgeYabs > corner_radius){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return pixel; - } - } - float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); - if(d = 0; -uniform int corner_radius_left< - string label = "Corner radius left"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_top< - string label = "Corner radius top"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int corner_radius_right< - string label = "Corner radius right"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; > = 0; -uniform float4 border_color; -uniform float border_alpha_start< - string label = "border alpha start"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 1.0; -uniform float border_alpha_end< - string label = "border alpha end"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float alpha_cut_off< - string label = "alpha cut off"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -float4 mainImage(VertData v_in) : TARGET -{ - float4 output_color = image.Sample(textureSampler, v_in.uv); - int closedEdgeX = 0; - int closedEdgeY = 0; - if(output_color.a < alpha_cut_off){ - return float4(1.0,0.0,0.0,0.0); - } - if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = corner_radius_right; - }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ - closedEdgeX = -corner_radius_left; - } - if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = corner_radius_bottom; - }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ - closedEdgeY = -corner_radius_top; - } - if(closedEdgeX == 0 && closedEdgeY == 0){ - return output_color; - } - if(closedEdgeX != 0){ - [loop] for(int x = 1;x corner_radius){ - closedEdgeXabs = 0; - } - if(closedEdgeYabs > corner_radius){ - closedEdgeYabs = 0; - } - if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ - return output_color; - } - if(closedEdgeXabs == 0){ - if(closedEdgeYabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; - } - } - if(closedEdgeYabs == 0){ - if(closedEdgeXabs <= border_thickness){ - float4 fade_color = border_color; - fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); - return fade_color; - }else{ - return output_color; +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) + +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; + +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; + +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; + +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; + +uniform texture2d background_layer < + string label = "Background Layer"; +>; + +float4 mainImage(VertData v_in) : TARGET +{ + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; + + float4 map_rgba = image.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; } - } - float closest = closedEdgeXabs 0.0) + base_rgba /= samples; + + base_rgba.a = transparent / count; + } + + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + + if (apply_alpha) { + float4 background_rgba = background_layer.Sample(textureSampler, v_in.uv); + + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); } ' } @@ -38159,25 +29888,110 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedRectShader { +function Get-OBSDisplacementMapAdvancedShader { -[Alias('Set-OBSRoundedRectShader','Add-OBSRoundedRectShader')] +[Alias('Set-OBSDisplacementMapAdvancedShader','Add-OBSDisplacementMapAdvancedShader')] param( -# Set the corner_radius of OBSRoundedRectShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedRectShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] +# Set the displacement_info of OBSDisplacementMapAdvancedShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] +[String] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapAdvancedShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapAdvancedShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the displacement_curve of OBSDisplacementMapAdvancedShader +[Alias('displacement_curve')] +[ComponentModel.DefaultBindingProperty('displacement_curve')] [Int32] -$BorderThickness, -# Set the border_color of OBSRoundedRectShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +$DisplacementCurve, +# Set the blur_info of OBSDisplacementMapAdvancedShader +[Alias('blur_info')] +[ComponentModel.DefaultBindingProperty('blur_info')] [String] -$BorderColor, +$BlurInfo, +# Set the blur_size of OBSDisplacementMapAdvancedShader +[Alias('blur_size')] +[ComponentModel.DefaultBindingProperty('blur_size')] +[Single] +$BlurSize, +# Set the blur_quality of OBSDisplacementMapAdvancedShader +[Alias('blur_quality')] +[ComponentModel.DefaultBindingProperty('blur_quality')] +[Single] +$BlurQuality, +# Set the blur_directions of OBSDisplacementMapAdvancedShader +[Alias('blur_directions')] +[ComponentModel.DefaultBindingProperty('blur_directions')] +[Single] +$BlurDirections, +# Set the blur_angle of OBSDisplacementMapAdvancedShader +[Alias('blur_angle')] +[ComponentModel.DefaultBindingProperty('blur_angle')] +[Single] +$BlurAngle, +# Set the chromatic_aberration_info of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration_info')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration_info')] +[String] +$ChromaticAberrationInfo, +# Set the chromatic_aberration of OBSDisplacementMapAdvancedShader +[Alias('chromatic_aberration')] +[ComponentModel.DefaultBindingProperty('chromatic_aberration')] +[Single] +$ChromaticAberration, +# Set the colorize_info of OBSDisplacementMapAdvancedShader +[Alias('colorize_info')] +[ComponentModel.DefaultBindingProperty('colorize_info')] +[String] +$ColorizeInfo, +# Set the colorize_color of OBSDisplacementMapAdvancedShader +[Alias('colorize_color')] +[ComponentModel.DefaultBindingProperty('colorize_color')] +[String] +$ColorizeColor, +# Set the flags_info of OBSDisplacementMapAdvancedShader +[Alias('flags_info')] +[ComponentModel.DefaultBindingProperty('flags_info')] +[String] +$FlagsInfo, +# Set the blue_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_strength')] +[ComponentModel.DefaultBindingProperty('blue_affects_strength')] +[Management.Automation.SwitchParameter] +$BlueAffectsStrength, +# Set the blue_affects_colorize of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_colorize')] +[ComponentModel.DefaultBindingProperty('blue_affects_colorize')] +[Management.Automation.SwitchParameter] +$BlueAffectsColorize, +# Set the blue_affects_blur of OBSDisplacementMapAdvancedShader +[Alias('blue_affects_blur')] +[ComponentModel.DefaultBindingProperty('blue_affects_blur')] +[Management.Automation.SwitchParameter] +$BlueAffectsBlur, +# Set the alpha_affects_strength of OBSDisplacementMapAdvancedShader +[Alias('alpha_affects_strength')] +[ComponentModel.DefaultBindingProperty('alpha_affects_strength')] +[Management.Automation.SwitchParameter] +$AlphaAffectsStrength, +# Set the apply_alpha of OBSDisplacementMapAdvancedShader +[Alias('apply_alpha')] +[ComponentModel.DefaultBindingProperty('apply_alpha')] +[Management.Automation.SwitchParameter] +$ApplyAlpha, +# Set the mask_layer of OBSDisplacementMapAdvancedShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] +[String] +$MaskLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38208,52 +30022,217 @@ $UseShaderTime process { -$shaderName = 'rounded_rect' -$ShaderNoun = 'OBSRoundedRectShader' +$shaderName = 'displacement_map_advanced' +$ShaderNoun = 'OBSDisplacementMapAdvancedShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement. You can choose the curve mapping to map values between -1 to 1 differently."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform int displacement_curve< + string label = "Displacement Curve"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Linear"; + int option_1_value = 2; + string option_1_label = "Quadratic"; + int option_2_value = 3; + string option_2_label = "Cubic"; > = 0; -uniform int border_thickness< - string label = "border thickness"; + +uniform string blur_info< + string label = "Blur"; + string widget_type = "info"; +> = "Imitates how light disperses through displacement. It can recreate refractions like effects. Blur size affects how much light disperses, quality is the number of samples, directions of those samples (around a circle). You can choose the starting angle (use directions 1 or 2 to have highly directional refractions)."; + +uniform float blur_size< + string label = "Blur Size (px)"; +> = 8.0; // BLUR SIZE (Radius) +uniform float blur_quality< + string label = "Blur Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 16; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float blur_directions< + string label = "Blur Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 24; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (number of rays in sampling) +uniform float blur_angle< + string label = "Blur Angle (degrees)"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; + float minimum = 0; + float maximum = 360; + float step = 1.0; +> = 0; // BLUR Angle (starting angle of first sample) -uniform float4 border_color; +uniform string chromatic_aberration_info< + string label = "Chromatic Aberration"; + string widget_type = "info"; +> = "Imitates how colors diverge when light refracts. Value is between -1 and 1 as a multiplier of the displacement amount."; + +uniform float chromatic_aberration< + string label = "Chromatic Aberration (0.0)"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; + +uniform string colorize_info< + string label = "Color"; + string widget_type = "info"; +> = "Imitates how light change color (tinted glass for instance)"; + +uniform float4 colorize_color< + string label = "Colorize"; +> = {0.0, 0.0, 0.0, 0.0}; + +uniform string flags_info< + string label = "Additional Channels"; + string widget_type = "info"; +> = "You can affect Blue channel (Height / Z) to displacement strength, colorization or blur radius, and Alpha to displacement (can fix glitches on edges)."; + +uniform bool blue_affects_strength< + string label = "Blue channel affects displacement"; +> = false; +uniform bool blue_affects_colorize< + string label = "Blue channel affects colorize"; +> = false; +uniform bool blue_affects_blur< + string label = "Blue channel affects blur"; +> = false; +uniform bool alpha_affects_strength< + string label = "Alpha channel affects displacement"; +> = false; +uniform bool apply_alpha< + string label = "Apply alpha"; +> = false; + +uniform texture2d mask_layer < + string label = "Mask Layer"; +>; float4 mainImage(VertData v_in) : TARGET { - float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); - float4 output_color = image.Sample(textureSampler, v_in.uv); - - float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); - float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); - - bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; - bool is_within_radius = pixel_distance_from_center <= corner_radius; - - bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); - bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); - - return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); -} + float Pi = 6.28318530718; // Pi*2 + float blurAngleRadians = (blur_angle / 360.) * Pi; -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} + float4 map_rgba = mask_layer.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map_rgba.r * 2) - 1, + (map_rgba.g * 2) - 1, + (map_rgba.b * 2) - 1, + map_rgba.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + + for(int p=1; p 0 && displace.a > 0) { + float4 oc = base_rgba; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=blurAngleRadians; d < Pi + blurAngleRadians; d+=Pi/blur_directions) { + [loop] for(float i=1.0 / blur_quality; i <= 1.0; i += 1.0 / blur_quality) { + float size = blur_size; + float4 sc; + + if (blue_affects_blur) { + size *= displace.b; + } + + if (chromatic_aberration) { + float4 sc_r = image.Sample(textureSampler, v_in.uv + displace_uv - chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_g = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + float4 sc_b = image.Sample(textureSampler, v_in.uv + displace_uv + chromatic_aberration*displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + + sc = float4(sc_r.r, sc_g.g, sc_b.b, sc_g.a); + } else { + sc = image.Sample(textureSampler, v_in.uv + displace_uv + float2(cos(d),sin(d)) * size * i / uv_size); + } + + transparent += sc.a; + count++; + base_rgba += sc * sc.a; + samples += sc.a; + } + } + + //Calculate averages + if (samples > 0.0) + base_rgba /= samples; + + base_rgba.a = transparent / count; + } + + if (blue_affects_colorize) { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a * displace.b); + } else { + base_rgba = lerp(base_rgba, float4(colorize_color.r, colorize_color.g, colorize_color.b, 1.0), colorize_color.a); + } + + if (apply_alpha) { + float4 background_rgba = image.Sample(textureSampler, v_in.uv); + + return lerp( + float4(background_rgba.r, background_rgba.g, background_rgba.b, background_rgba.a), + float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a), + displace.a + ); + } + + return float4(base_rgba.r, base_rgba.g, base_rgba.b, base_rgba.a * displace.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} switch -regex ($myVerb) { Get { $FilterNamePattern = "(?>$( @@ -38344,54 +30323,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeGradientShader { +function Get-OBSDisplacementMapInvertShader { -[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] +[Alias('Set-OBSDisplacementMapInvertShader','Add-OBSDisplacementMapInvertShader')] param( -# Set the corner_radius of OBSRoundedStrokeGradientShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeGradientShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the rotation_speed of OBSRoundedStrokeGradientShader -[Alias('rotation_speed')] -[ComponentModel.DefaultBindingProperty('rotation_speed')] -[Int32] -$RotationSpeed, -# Set the border_colorL of OBSRoundedStrokeGradientShader -[Alias('border_colorL')] -[ComponentModel.DefaultBindingProperty('border_colorL')] -[String] -$BorderColorL, -# Set the border_colorR of OBSRoundedStrokeGradientShader -[Alias('border_colorR')] -[ComponentModel.DefaultBindingProperty('border_colorR')] +# Set the displacement_info of OBSDisplacementMapInvertShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] [String] -$BorderColorR, -# Set the center_width of OBSRoundedStrokeGradientShader -[Alias('center_width')] -[ComponentModel.DefaultBindingProperty('center_width')] -[Int32] -$CenterWidth, -# Set the center_height of OBSRoundedStrokeGradientShader -[Alias('center_height')] -[ComponentModel.DefaultBindingProperty('center_height')] -[Int32] -$CenterHeight, -# Set the notes of OBSRoundedStrokeGradientShader -[ComponentModel.DefaultBindingProperty('notes')] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapInvertShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapInvertShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the background_layer of OBSDisplacementMapInvertShader +[Alias('background_layer')] +[ComponentModel.DefaultBindingProperty('background_layer')] [String] -$Notes, +$BackgroundLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38422,198 +30377,44 @@ $UseShaderTime process { -$shaderName = 'rounded_stroke_gradient' -$ShaderNoun = 'OBSRoundedStrokeGradientShader' +$shaderName = 'displacement_map_invert' +$ShaderNoun = 'OBSDisplacementMapInvertShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "Border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int rotation_speed< - string label = "rotation speed"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform float4 border_colorL; -uniform float4 border_colorR; -//uniform float color_spread = 2.0; -uniform int center_width< - string label = "center width"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_height< - string label = "center height"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; - -// float3 hsv2rgb(float3 c) -// { -// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); -// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); -// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); -// } +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the Background Layer with the current image. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; -float mod(float x, float y) -{ - return x - y * floor(x/y); -} +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; -float4 gradient(float c) { - c = mod(c , 2.0); - if(c < 0.0f){ - c = c * -1.0; - } - if(c > 1.0){ - c = 1.0 - c; - if(c < 0.0f){ - c = c + 1.0; - } - } - return lerp(border_colorL, border_colorR, c); -} +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; -float4 getBorderColor(float2 toCenter){ - float angle = atan2(toCenter.y ,toCenter.x ); - float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; - return gradient((angle / 3.14159265f) + angleMod); -} +uniform texture2d background_layer ; float4 mainImage(VertData v_in) : TARGET { - float2 st = v_in.uv * uv_scale; - float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); - float2 toCenter = center_pixel_coordinates - st; + float4 map = image.Sample(textureSampler, v_in.uv); + float4 base = background_layer.Sample(textureSampler, v_in.uv); - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return output_color; - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return getBorderColor(toCenter); - } - else - { - return output_color; - } - } - return float4(0.0, 0.0, 0.0, 0.0); + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = background_layer.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -38711,34 +30512,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRoundedStrokeShader { +function Get-OBSDisplacementMapShader { -[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] +[Alias('Set-OBSDisplacementMapShader','Add-OBSDisplacementMapShader')] param( -# Set the corner_radius of OBSRoundedStrokeShader -[Alias('corner_radius')] -[ComponentModel.DefaultBindingProperty('corner_radius')] -[Int32] -$CornerRadius, -# Set the border_thickness of OBSRoundedStrokeShader -[Alias('border_thickness')] -[ComponentModel.DefaultBindingProperty('border_thickness')] -[Int32] -$BorderThickness, -# Set the minimum_alpha_percent of OBSRoundedStrokeShader -[Alias('minimum_alpha_percent')] -[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] -[Int32] -$MinimumAlphaPercent, -# Set the border_color of OBSRoundedStrokeShader -[Alias('border_color')] -[ComponentModel.DefaultBindingProperty('border_color')] +# Set the displacement_info of OBSDisplacementMapShader +[Alias('displacement_info')] +[ComponentModel.DefaultBindingProperty('displacement_info')] [String] -$BorderColor, -# Set the notes of OBSRoundedStrokeShader -[ComponentModel.DefaultBindingProperty('notes')] +$DisplacementInfo, +# Set the displacement_x of OBSDisplacementMapShader +[Alias('displacement_x')] +[ComponentModel.DefaultBindingProperty('displacement_x')] +[Single] +$DisplacementX, +# Set the displacement_y of OBSDisplacementMapShader +[Alias('displacement_y')] +[ComponentModel.DefaultBindingProperty('displacement_y')] +[Single] +$DisplacementY, +# Set the mask_layer of OBSDisplacementMapShader +[Alias('mask_layer')] +[ComponentModel.DefaultBindingProperty('mask_layer')] [String] -$Notes, +$MaskLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -38769,167 +30566,71 @@ $UseShaderTime process { -$shaderName = 'rounded_stroke' -$ShaderNoun = 'OBSRoundedStrokeShader' +$shaderName = 'displacement_map' +$ShaderNoun = 'OBSDisplacementMapShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader -//modified slightly by Surn -//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 -uniform int corner_radius< - string label = "Corner radius"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 200; - int step = 1; -> = 0; -uniform int border_thickness< - string label = "border thickness"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform int minimum_alpha_percent< - string label = "Minimum alpha percent"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float4 border_color; -uniform string notes< - string widget_type = "info"; -> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; +uniform string displacement_info< + string label = "Displacement"; + string widget_type = "info"; +> = "Displaces the current image with the Mask Layer. Red channel is affected to X (horizontal) displacement and Green channel to Y (vertical). rgb(.5, .5, .5) is no displacement."; + +uniform float displacement_x< + string label = "Displacement X (px)"; +> = 16.0; + +uniform float displacement_y< + string label = "Displacement Y (px)"; +> = 16.0; + +uniform texture2d mask_layer ; float4 mainImage(VertData v_in) : TARGET { - float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); - float4 output_color = image.Sample(textureSampler, v_in.uv); - if (output_color.a < min_alpha) - { - return float4(0.0, 0.0, 0.0, 0.0); - } - int closedEdgeX = 0; - if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = corner_radius; - } - int closedEdgeY = 0; - if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = corner_radius; - } - if (closedEdgeX == 0 && closedEdgeY == 0) - { - return float4(output_color); - } - if (closedEdgeX != 0) - { - [loop] - for (int x = 1; x < corner_radius; x++) - { - if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) - { - closedEdgeX = x; - break; - } - } - } - if (closedEdgeY != 0) - { - [loop] - for (int y = 1; y < corner_radius; y++) - { - if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) - { - closedEdgeY = y; - break; - } - } - } - if (closedEdgeX == 0) - { - if (closedEdgeY < border_thickness) - { - return border_color; - } - else - { - return float4(output_color); - } - } - if (closedEdgeY == 0) - { - if (closedEdgeX < border_thickness) - { - return border_color; - } - else - { - return float4(output_color); - } - } - - float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); - if (d < corner_radius) - { - if (corner_radius - d < border_thickness) - { - return border_color; - } - else - { - return output_color; - } - } - return float4(0.0, 0.0, 0.0, 0.0); -} -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } + float4 map = mask_layer.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + + float2 displace_strength = float2(displacement_x, displacement_y) / uv_size; + float4 displace = float4( + (map.r * 2) - 1, + (map.g * 2) - 1, + (map.b * 2) - 1, + map.a + ); + + float2 displace_uv = float2(displace.r, displace.g) * displace_strength; + float4 displaced = image.Sample(textureSampler, v_in.uv + displace_uv); + + return float4(displaced.r, displaced.g, displaced.b, displaced.a * displace.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } } 'Remove' { if ($SourceName) { @@ -39000,43 +30701,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScanLineShader { +function Get-OBSDivideRotateShader { -[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] +[Alias('Set-OBSDivideRotateShader','Add-OBSDivideRotateShader')] param( -# Set the lengthwise of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('lengthwise')] -[Management.Automation.SwitchParameter] -$Lengthwise, -# Set the animate of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the speed of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the angle of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the shift of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('shift')] -[Management.Automation.SwitchParameter] -$Shift, -# Set the boost of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('boost')] +# Set the iChannel0 of OBSDivideRotateShader +[ComponentModel.DefaultBindingProperty('iChannel0')] +[String] +$IChannel0, +# Set the speed_percentage of OBSDivideRotateShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSDivideRotateShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSDivideRotateShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] [Management.Automation.SwitchParameter] -$Boost, -# Set the floor of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('floor')] -[Single] -$Floor, -# Set the period of OBSScanLineShader -[ComponentModel.DefaultBindingProperty('period')] -[Single] -$Period, -# Set the notes of OBSScanLineShader +$ApplyToAlphaLayer, +# Set the notes of OBSDivideRotateShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -39070,106 +30758,90 @@ $UseShaderTime process { -$shaderName = 'scan_line' -$ShaderNoun = 'OBSScanLineShader' +$shaderName = 'divide_rotate' +$ShaderNoun = 'OBSDivideRotateShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Scan Line Effect for OBS Studio -// originally from Andersama (https://github.com/Andersama) -// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -//Count the number of scanlines we want via height or width, adjusts the sin wave period -uniform bool lengthwise; -//Do we want the scanlines to move? -uniform bool animate; -//How fast do we want those scanlines to move? -uniform float speed< +// divide and rotate shader for OBS Studio shaderfilter plugin +// originally from shadertoy (https://www.shadertoy.com/view/3sy3Dh) +// Modified by Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform texture2d iChannel0; +uniform int speed_percentage< string label = "Speed"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10000.0; - float step = 1; -> = 1000; -//What angle should the scanlines come in at (based in degrees) -uniform float angle< - string label = "angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 45; -//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit -//By default values for color range from 0 to 1 -//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen -uniform bool shift = true; -uniform bool boost = true; -//Increases the minimum value of the sin wave -uniform float floor< - string label = "Floor"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.001; -> = 0.0; -//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input -uniform float period< - string label = "Period"; + int minimum = -10; + int maximum = 10; + int step = 1; +> = 5; +uniform int alpha_percentage< + string label = "Opacity Percentage"; string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 1.0; -> = 10.0; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = true; + uniform string notes< string widget_type = "info"; -> = "floor affects the minimum opacity of the scan line"; +> = "add rotation and speed"; + + +float2 cm(float2 a, float2 b) { + return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +float2 iter(float2 uv, float2 rot, float scale) { + float2 gv = frac(cm(uv, rot) * scale); + float boundDist = 1. - max(abs(gv.x), abs(gv.y)); + float mask = step(.03, boundDist); + gv *= mask; + return gv; +} + float4 mainImage(VertData v_in) : TARGET { - //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 - // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 - float nfloor = clamp(floor, 0.0, 100.0) * 0.01; - float nperiod = max(period, 1.0); - float gap = 1 - nfloor; - float pi = 3.1415926535897932384626; - float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); - float nspeed = 0.0; - if(animate){ - nspeed = speed * 0.0001; - } - - float4 color = image.Sample(textureSampler, v_in.uv); - - float t = elapsed_time * nspeed; - - if(!lengthwise){ - float base_height = 1.0 / uv_pixel_interval.y; - float h_interval = pi * base_height; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } - else{ - float base_width = 1.0 / uv_pixel_interval.x; - float w_interval = pi * base_width; - - float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); - if(shift){ - rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; - if(boost){ - rh_sin += gap * 0.5; - } - } - float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); - return s_mult * color; - } -} + float alpha = clamp(alpha_percentage * 0.01, 0.0, 1.0); + float speed = clamp(speed_percentage * 0.01, -10.0, 10.0); + + // Normalize coords + //float2 uv = (v_in.uv * uv_scale + uv_offset); + float2 uv = (float2(v_in.uv.x, (1 - v_in.uv.y)) * uv_scale + uv_offset) - .5 * (v_in.uv * uv_scale + uv_offset);// / v_in.uv.y; + float2 mouse = (v_in.uv.xy - .5 * v_in.uv.xy) / v_in.uv.y; + + // Add some time rotation and offset + float t = elapsed_time * speed; + float2 time = float2(sin(t), cos(t)); + uv += time; + + // Imaginary component has to be mirrored for natural feeling rotation + mouse.y *= -1.0; + + // Draw few layers of this to bend space + float2 rot = cm(mouse, time); + for (float i=1.0; i<=3.0; i++) { + uv = iter(uv, rot, 1.5); + } + + // Combine background with new image + float4 background_color = image.Sample(textureSampler, v_in.uv); + float4 col = iChannel0.Sample(textureSampler, uv); + + // Border + if (uv.x == 0.0 && uv.y == 0.0) { + col = float4(0,0,0,alpha); + } + + // if not appling to alpha layer, set output alpha + if (Apply_To_Alpha_Layer == false) + col.a = alpha; + + //output color is combined with background image + col.rgb = lerp(background_color.rgb,col.rgb,clamp(alpha, 0.0, 1.0)); + return col; +} ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -39267,54 +30939,62 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeascapeShader { +function Get-OBSDoodleShader { -[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] +[Alias('Set-OBSDoodleShader','Add-OBSDoodleShader')] param( -# Set the AA of OBSSeascapeShader -[ComponentModel.DefaultBindingProperty('AA')] -[Management.Automation.SwitchParameter] -$AA, -# Set the SEA_HEIGHT of OBSSeascapeShader -[Alias('SEA_HEIGHT')] -[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] +# Set the ViewProj of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSDoodleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$SEAHEIGHT, -# Set the SEA_CHOPPY of OBSSeascapeShader -[Alias('SEA_CHOPPY')] -[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] +$ElapsedTime, +# Set the uv_offset of OBSDoodleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSDoodleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSDoodleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSDoodleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$SEACHOPPY, -# Set the SEA_SPEED of OBSSeascapeShader -[Alias('SEA_SPEED')] -[ComponentModel.DefaultBindingProperty('SEA_SPEED')] +$RandF, +# Set the uv_size of OBSDoodleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the Doodle_Scale_Percent of OBSDoodleShader +[Alias('Doodle_Scale_Percent')] +[ComponentModel.DefaultBindingProperty('Doodle_Scale_Percent')] [Single] -$SEASPEED, -# Set the SEA_FREQ of OBSSeascapeShader -[Alias('SEA_FREQ')] -[ComponentModel.DefaultBindingProperty('SEA_FREQ')] +$DoodleScalePercent, +# Set the Snap_Percent of OBSDoodleShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] [Single] -$SEAFREQ, -# Set the SEA_BASE of OBSSeascapeShader -[Alias('SEA_BASE')] -[ComponentModel.DefaultBindingProperty('SEA_BASE')] -[String] -$SEABASE, -# Set the SEA_WATER_COLOR of OBSSeascapeShader -[Alias('SEA_WATER_COLOR')] -[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] +$SnapPercent, +# Set the Notes of OBSDoodleShader +[ComponentModel.DefaultBindingProperty('Notes')] [String] -$SEAWATERCOLOR, -# Set the CAMERA_SPEED of OBSSeascapeShader -[Alias('CAMERA_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] -[Single] -$CAMERASPEED, -# Set the CAMERA_TURN_SPEED of OBSSeascapeShader -[Alias('CAMERA_TURN_SPEED')] -[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] -[Single] -$CAMERATURNSPEED, +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39345,282 +31025,96 @@ $UseShaderTime process { -$shaderName = 'seascape' -$ShaderNoun = 'OBSSeascapeShader' +$shaderName = 'doodle' +$ShaderNoun = 'OBSDoodleShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -/* - * "Seascape" by Alexander Alekseev aka TDM - 2014 - * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. - * Contact: tdmaav@gmail.com - * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro - */ - -#define NUM_STEPS 8 -#define PI 3.141592 -#define EPSILON 0.001 -uniform bool AA< - string label = "Smooth (more resources)"; -> = false; +// doodle effect by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +uniform float4x4 ViewProj; +uniform texture2d image; -#ifndef OPENGL -#define mat2 float2x2 -#define mat3 float3x3 -#define fract frac -#define mix lerp -#endif +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; -// sea -#define ITER_GEOMETRY 3 -#define ITER_FRAGMENT 5 -uniform float SEA_HEIGHT< - string label = "Sea Height"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.5; - float step = 0.001; -> = 0.6; -uniform float SEA_CHOPPY< - string label = "Sea Choppy"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 4.0; -uniform float SEA_SPEED< - string label = "Sea Speed"; +uniform float Doodle_Scale_Percent< + string label = "Doodle Scale Percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.8; -uniform float SEA_FREQ< - string label = "Sea Frequency"; + float maximum = 100.0; + float step = 0.1; +> = 2.5; +uniform float Snap_Percent< + string label = "Snap Percent"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.16; -uniform float4 SEA_BASE< - string label = "Sea Base"; -> = {0.0,0.09,0.18,1.0}; -uniform float4 SEA_WATER_COLOR< - string label = "Sea Water"; -> = {0.48,0.54,0.36,1.0}; + float minimum = 1.0; + float maximum = 100.0; + float step = 0.1; +> = 7.5; +uniform string Notes< + string widget_type = "info"; +> = "Doodle skews the image by the Scale Percent, Snap Percent controls the number of doodles per second."; -uniform float CAMERA_SPEED< - string label = "Camera Speed"; - string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; - -uniform float CAMERA_TURN_SPEED< - string label = "Camera Turn Speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; - - -float SEA_TIME(){ - return 1.0 + elapsed_time * SEA_SPEED; -} - -// math -mat3 fromEuler(float3 ang) { - float2 a1 = float2(sin(ang.x),cos(ang.x)); - float2 a2 = float2(sin(ang.y),cos(ang.y)); - float2 a3 = float2(sin(ang.z),cos(ang.z)); - return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), - float3(-a2.y*a1.x,a1.y*a2.y,a2.x), - float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); -} - -float hash(float2 p) { - float h = dot(p,float2(127.1,311.7)); - return fract(sin(h)*43758.5453123); -} - -float noise(float2 p) { - float2 i = floor( p ); - float2 f = fract( p ); - float2 u = f*f*(3.0-2.0*f); - return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), - hash( i + float2(1.0,0.0) ), u.x), - mix( hash( i + float2(0.0,1.0) ), - hash( i + float2(1.0,1.0) ), u.x), u.y); -} - -// lighting -float diffuse(float3 n,float3 l,float p) { - return pow(dot(n,l) * 0.4 + 0.6,p); -} -float specular(float3 n,float3 l,float3 e,float s) { - float nrm = (s + 8.0) / (PI * 8.0); - return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; -} - -// sky -float3 getSkyColor(float3 e) { - e.y = (max(e.y,0.0)*0.8+0.2)*0.8; - return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; -} - -// sea -float sea_octave(float2 uv, float choppy) { - uv += noise(uv); - float2 wv = 1.0-abs(sin(uv)); - float2 swv = abs(cos(uv)); - wv = mix(wv,swv,wv); - return pow(1.0-pow(wv.x * wv.y,0.65),choppy); -} - -float map(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; - uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_GEOMETRY; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); - } - return p.y - h; -} +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -float map_detailed(float3 p) { - float freq = SEA_FREQ; - float amp = SEA_HEIGHT; - float choppy = SEA_CHOPPY; - float2 uv = p.xz; uv.x *= 0.75; - mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); - float st = SEA_TIME(); - float d, h = 0.0; - for(int i = 0; i < ITER_FRAGMENT; i++) { - d = sea_octave((uv+float2(st,st))*freq,choppy); - d += sea_octave((uv-float2(st,st))*freq,choppy); - h += d * amp; - uv = mul(uv, octave_m); - freq *= 1.9; - amp *= 0.22; - choppy = mix(choppy,1.0,0.2); - } - return p.y - h; +float3 rand3(float3 co) +{ + float j = 4096.0*sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = frac(512.0*j); + j *= .125; + result.x = frac(512.0*j); + j *= .125; + result.y = frac(512.0*j); + return result - 0.5; } -float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { - float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); - fresnel = min(pow(fresnel,3.0), 0.5); - - float3 reflected = getSkyColor(reflect(eye,n)); - float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; - - float3 color = mix(refracted,reflected,fresnel); - - float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); - color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; - - float s = specular(n,l,eye,60.0); - color += float3(s,s,s); - - return color; +float snap(float x, float snap) +{ + return snap * round(x / max(0.01,snap)); } -// tracing -float3 getNormal(float3 p, float eps) { - float3 n; - n.y = map_detailed(p); - n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; - n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; - n.y = eps; - return normalize(n); -} +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + float time = snap((1 + sin(elapsed_time)) * 0.5, Snap_Percent * .01); + float rand = snap(rand_f, Snap_Percent *.01); + float2 noise = rand3(v_in.pos.xyz + float3(time,0.0,0.0)).xy * (Doodle_Scale_Percent * .01); + vert_out.uv.xy += noise; -float heightMapTracing(float3 ori, float3 dir, out float3 p) { - float tm = 0.0; - float tx = 1000.0; - float hx = map(ori + dir * tx); - if(hx > 0.0) { - p = ori + dir * tx; - return tx; - } - float hm = map(ori + dir * tm); - float tmid = 0.0; - for(int i = 0; i < NUM_STEPS; i++) { - tmid = mix(tm,tx, hm/(hm-hx)); - p = ori + dir * tmid; - float hmid = map(p); - if(hmid < 0.0) { - tx = tmid; - hx = hmid; - } else { - tm = tmid; - hm = hmid; - } - } - return tmid; + return vert_out; } -float3 getPixel(in float2 coord, float time) { - float2 uv = coord / uv_size.xy; - uv = uv * 2.0 - 1.0; - uv.x *= uv_size.x / uv_size.y; - - // ray - float3 ang = float3(sin(time*3.0*CAMERA_TURN_SPEED)*0.1,sin(time*CAMERA_TURN_SPEED)*0.2+0.3,time*CAMERA_TURN_SPEED); - float3 ori = float3(0.0,3.5,time*5.0*CAMERA_SPEED); - float3 dir = normalize(float3(uv.xy,-2.0)); - dir.z += length(uv) * 0.14; - dir = mul(normalize(dir), fromEuler(ang)); - - // tracing - float3 p; - heightMapTracing(ori,dir,p); - float3 dist = p - ori; - float3 n = getNormal(p, dot(dist,dist) * (0.1 / uv_size.x)); - float3 light = normalize(float3(0.0,1.0,0.8)); - - // color - return mix( - getSkyColor(dir), - getSeaColor(p,n,light,dir,dist), - pow(smoothstep(0.0,-0.02,dir.y),0.2)); +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); } -// main -float4 mainImage(VertData v_in) : TARGET +technique Draw { - float time = elapsed_time * 0.3; - float2 fragCoord = float2(v_in.uv.x * uv_size.x, (1.0 - v_in.uv.y) * uv_size.y); - - float3 color = float3(0.0,0.0,0.0);; - if (AA){ - for(int i = -1; i <= 1; i++) { - for(int j = -1; j <= 1; j++) { - float2 uv = fragCoord+float2(i,j)/3.0; - color += getPixel(uv, time); - } - } - color /= 9.0; - }else{ - color = getPixel(fragCoord, time); - } - - // post - return float4(pow(color,float3(0.65,0.65,0.65)), 1.0); + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -39718,30 +31212,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSeasickShader { +function Get-OBSDrawingsShader { -[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] +[Alias('Set-OBSDrawingsShader','Add-OBSDrawingsShader')] param( -# Set the notes of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, -# Set the amplitude of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the speed of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the frequency of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('frequency')] -[Single] -$Frequency, -# Set the opacity of OBSSeasickShader -[ComponentModel.DefaultBindingProperty('opacity')] -[Single] -$Opacity, +# Set the AngleNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('AngleNum')] +[Int32] +$AngleNum, +# Set the SampNum of OBSDrawingsShader +[ComponentModel.DefaultBindingProperty('SampNum')] +[Int32] +$SampNum, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -39772,51 +31254,144 @@ $UseShaderTime process { -$shaderName = 'seasick' -$ShaderNoun = 'OBSSeasickShader' +$shaderName = 'drawings' +$ShaderNoun = 'OBSDrawingsShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Seasick - an effect for OBS Studio -// -uniform string notes< - string widget_type = "info"; -> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; -uniform float amplitude< - string label = "amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.03; -uniform float speed< - string label = "speed"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 1.0; -uniform float frequency< - string label = "frequency"; +//based on https://www.shadertoy.com/view/ldlcWs + +uniform int AngleNum< + string label = "Number of angles"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 0.01; -> = 6.0; -uniform float opacity< - string label = "opacity"; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 3; +uniform int SampNum< + string label = "Number of samples"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; + int minimum = 0.0; + int maximum = 25; + int step = 1; +> = 9; -float4 mainImage(VertData v_in) : TARGET +float4 getCol(float2 pos) { - float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); - float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); - return image.Sample(textureSampler, coord) * opacity; + // take aspect ratio into account + float2 uv=pos; + float4 c1=image.Sample(textureSampler, uv); + float4 e=smoothstep(float4(-0.05,-0.05,-0.05,-0.05),float4(-0.0,-0.0,-0.0,-0.0),float4(uv,float2(1,1)-uv)); + c1=lerp(float4(1,1,1,0),c1,e.x*e.y*e.z*e.w); + float d=clamp(dot(c1.xyz,float3(-.5,1.,-.5)),0.0,1.0); + float4 c2=float4(.7,.7,.7,.7); + return min(lerp(c1,c2,1.8*d),.7); +} + +float4 getColHT(float2 pos) +{ + return smoothstep(0.795,1.05,getCol(pos)*.8+.2+1.0); +} + +float getVal(float2 pos) +{ + float4 c=getCol(pos); + return pow(dot(c.xyz,float3(.333,.333,.333)),1.)*1.; +} + +float2 getGrad(float2 pos, float eps) +{ + float2 d=float2(eps,0.); + return float2( + getVal(pos+d.xy)-getVal(pos-d.xy), + getVal(pos+d.yx)-getVal(pos-d.yx) + )/eps/2.; } + + float lum( float3 c) { + return dot(c, float3(0.3, 0.59, 0.11)); + } + + + float3 clipcolor( float3 c) { + float l = lum(c); + float n = min(min(c.r, c.g), c.b); + float x = max(max(c.r, c.g), c.b); + + if (n < 0.0) { + c.r = l + ((c.r - l) * l) / (l - n); + c.g = l + ((c.g - l) * l) / (l - n); + c.b = l + ((c.b - l) * l) / (l - n); + } + if (x > 1.25) { + c.r = l + ((c.r - l) * (1.0 - l)) / (x - l); + c.g = l + ((c.g - l) * (1.0 - l)) / (x - l); + c.b = l + ((c.b - l) * (1.0 - l)) / (x - l); + } + return c; + } + + float3 setlum( float3 c, float l) { + float d = l - lum(c); + c = c + float3(d,d,d); + return clipcolor(0.85*c); + } + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + float3 col = float3(0,0,0); + float3 col2 = float3(0,0,0); + float sum=0.; + + for(int i=0;i = 0.40; -uniform float cutoff_Green< - string label = "cutoff Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.025; -uniform float cutoff_Blue< - string label = "cutoff Blue"; +// Drop Shadow shader modified by Charles Fettinger +// impose a limiter to keep from crashing the system +//Converted to OpenGL by Exeldro February 19, 2022 +uniform int shadow_offset_x< + string label = "Shadow offset x"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float cutoff_Yellow< - string label = "cutoff Yellow"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_offset_y< + string label = "Shadow offset y"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.25; -uniform float acceptance_Amplifier< - string label = "acceptance Amplifier"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 5; +uniform int shadow_blur_size< + string label = "Shadow blur size"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 20.0; - float step = 0.001; -> = 5.0; - -uniform bool show_Red = true; -uniform bool show_Green = true; -uniform bool show_Blue = true; -uniform bool show_Yellow = true; + int minimum = 0; + int maximum = 15; + int step = 1; +> = 3; uniform string notes< string widget_type = "info"; -> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; -uniform int background_type< - string label = "background type"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "Grey"; - int option_1_value = 1; - string option_1_label = "Luma"; - int option_2_value = 2; - string option_2_label = "White"; - int option_3_value = 3; - string option_3_label = "Black"; - int option_4_value = 4; - string option_4_label = "Transparent"; - int option_5_value = 5; - string option_5_label = "Background Color"; -> = 0; - -float4 mainImage(VertData v_in) : TARGET -{ - const float PI = 3.1415926535897932384626433832795;//acos(-1); - const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); - float4 color = image.Sample(textureSampler, v_in.uv); - - float luminance = dot(coefLuma, color.rgb); - float4 gray = float4(luminance, luminance, luminance, 1.0); - - if (background_type == 0) - { - luminance = (color.r + color.g + color.b) * 0.3333; - gray = float4(luminance,luminance,luminance, 1.0); - } - //if (background_type == 1) - //{ - // gray = float4(luminance,luminance,luminance, 1.0); - //} - if (background_type == 2) - gray = float4(1.0,1.0,1.0,1.0); - if (background_type == 3) - gray = float4(0.0,0.0,0.0,1.0); - if (background_type == 4) - gray.a = 0.01; - if (background_type == 5) - gray = color; - - float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); - float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); - float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); - - float rgLuminance = (color.r*1.4 + color.g*0.6)/2; - float rgDiff = abs(color.r-color.g)*1.4; - - float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; - - float4 accept; - accept.r = float(show_Red) * (redness - cutoff_Red); - accept.g = float(show_Green) * (greenness - cutoff_Green); - accept.b = float(show_Blue) * (blueness - cutoff_Blue); - accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); +> = "blur size is limited to a max of 15 to ensure GPU"; - float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); - float modAcceptance = min (acceptance * acceptance_Amplifier, 1); +uniform float4 shadow_color; - float4 result = color; - if (result.a > 0) { - result = modAcceptance * color + (1.0 - modAcceptance) * gray; - //result.a *= gray.a; - } +uniform bool is_alpha_premultiplied; - return result; +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_size_limited = max(0, min(15, shadow_blur_size)); + int shadow_blur_samples = int(pow(float(shadow_blur_size_limited * 2 + 1), 2.0)); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * float(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * float(shadow_offset_y)); + + float sampled_shadow_alpha = 0.0; + + for (int blur_x = -shadow_blur_size_limited; blur_x <= shadow_blur_size_limited; blur_x++) + { + for (int blur_y = -shadow_blur_size_limited; blur_y <= shadow_blur_size_limited; blur_y++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * float(blur_x), uv_pixel_interval.y * float(blur_y)); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a / float(shadow_blur_samples); + } + } + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * sampled_shadow_alpha); + return final_shadow_color * (1.0-color.a) + color * (is_alpha_premultiplied?color.a:1.0); } ' @@ -40221,82 +31709,58 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShakeShader { +function Get-OBSDrunkShader { -[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] +[Alias('Set-OBSDrunkShader','Add-OBSDrunkShader')] param( -# Set the ViewProj of OBSShakeShader -[ComponentModel.DefaultBindingProperty('ViewProj')] +# Set the color_matrix of OBSDrunkShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] [Single[][]] -$ViewProj, -# Set the image of OBSShakeShader -[ComponentModel.DefaultBindingProperty('image')] +$ColorMatrix, +# Set the glow_percent of OBSDrunkShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSDrunkShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSDrunkShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed_percent of OBSDrunkShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the Apply_To_Alpha_Layer of OBSDrunkShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the glow_color of OBSDrunkShader +[Alias('glow_color')] +[ComponentModel.DefaultBindingProperty('glow_color')] [String] -$Image, -# Set the elapsed_time of OBSShakeShader -[Alias('elapsed_time')] -[ComponentModel.DefaultBindingProperty('elapsed_time')] -[Single] -$ElapsedTime, -# Set the uv_offset of OBSShakeShader -[Alias('uv_offset')] -[ComponentModel.DefaultBindingProperty('uv_offset')] -[Single[]] -$UvOffset, -# Set the uv_scale of OBSShakeShader -[Alias('uv_scale')] -[ComponentModel.DefaultBindingProperty('uv_scale')] -[Single[]] -$UvScale, -# Set the uv_pixel_interval of OBSShakeShader -[Alias('uv_pixel_interval')] -[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] -[Single[]] -$UvPixelInterval, -# Set the rand_f of OBSShakeShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSShakeShader -[Alias('uv_size')] -[ComponentModel.DefaultBindingProperty('uv_size')] -[Single[]] -$UvSize, -# Set the local_time of OBSShakeShader -[Alias('local_time')] -[ComponentModel.DefaultBindingProperty('local_time')] -[Single] -$LocalTime, -# Set the random_scale of OBSShakeShader -[Alias('random_scale')] -[ComponentModel.DefaultBindingProperty('random_scale')] -[Single] -$RandomScale, -# Set the worble of OBSShakeShader -[ComponentModel.DefaultBindingProperty('worble')] +$GlowColor, +# Set the ease of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('ease')] [Management.Automation.SwitchParameter] -$Worble, -# Set the speed of OBSShakeShader -[ComponentModel.DefaultBindingProperty('speed')] -[Single] -$Speed, -# Set the min_growth_pixels of OBSShakeShader -[Alias('min_growth_pixels')] -[ComponentModel.DefaultBindingProperty('min_growth_pixels')] -[Single] -$MinGrowthPixels, -# Set the max_growth_pixels of OBSShakeShader -[Alias('max_growth_pixels')] -[ComponentModel.DefaultBindingProperty('max_growth_pixels')] -[Single] -$MaxGrowthPixels, -# Set the randomize_movement of OBSShakeShader -[Alias('randomize_movement')] -[ComponentModel.DefaultBindingProperty('randomize_movement')] +$Ease, +# Set the glitch of OBSDrunkShader +[ComponentModel.DefaultBindingProperty('glitch')] [Management.Automation.SwitchParameter] -$RandomizeMovement, -# Set the notes of OBSShakeShader +$Glitch, +# Set the notes of OBSDrunkShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -40330,157 +31794,171 @@ $UseShaderTime process { -$shaderName = 'shake' -$ShaderNoun = 'OBSShakeShader' +$shaderName = 'drunk' +$ShaderNoun = 'OBSDrunkShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 -// Added some randomization based upon random_scale input -// updated for latest version of obs-shaderfilter - -uniform float4x4 ViewProj; -uniform texture2d image; - -uniform float elapsed_time; -uniform float2 uv_offset; -uniform float2 uv_scale; -uniform float2 uv_pixel_interval; -uniform float rand_f; -uniform float2 uv_size; -uniform float local_time; +// Drunk shader by Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +uniform float4x4 color_matrix; -uniform float random_scale< - string label = "random scale"; +uniform int glow_percent< + string label = "Glow percent"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 0.25; -uniform bool worble = false; -uniform float speed< - string label = "Speed"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.001; -> = 1.0; -uniform float min_growth_pixels< - string label = "min growth pixels"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = -2.0; -uniform float max_growth_pixels< - string label = "max growth pixels"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; string widget_type = "slider"; - float minimum = -10.0; - float maximum = 10.0; - float step = 0.001; -> = 2.0; -uniform bool randomize_movement = false; - + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform float4 glow_color; +uniform bool ease; +uniform bool glitch; uniform string notes< string widget_type = "info"; -> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; +> ="''drunk refers to the bad blur effect of using 4 coordinates to blur. ''blur'' - the distance between the 4 copies (recommend 1-4)"; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; +// Gaussian Blur +float Gaussian(float x, float o) { + const float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o))); +} -//noise values in range if 0.0 to 1.0 -float noise3D(float x, float y, float z) { - float ptr = 0.0f; - return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; } -VertData mainTransform(VertData v_in) +float BlurStyler(float t,float b,float c,float d,bool ease) { - VertData vert_out; + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; +} - float3 pos = v_in.pos.xyz; - float t; - float s; - float noise; - if (randomize_movement) - { - t = (rand_f * 2) - 1.0f; - s = (1 - rand_f * 2) - 1.0f;; - noise = clamp( rand_f * random_scale,-0.99, 0.99); - } - else - { - t = (1 + sin(elapsed_time * speed)) / 2; - s = (1 + cos(elapsed_time * speed)) / 2; - noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; } + return l_value; +} - float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); - float3 min_pos; - float3 max_pos; - if (worble) - { - min_pos = pos + direction_from_center * min_growth_pixels * 0.5; - max_pos = pos + direction_from_center * max_growth_pixels * 0.5; - } - else - { - min_pos = pos + direction_from_center * 0.5; - max_pos = min_pos; - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 offsets[4]; + offsets[0] = float2(-0.05, 0.066); + offsets[1] = float2(-0.05, -0.066); + offsets[2] = float2(0.05, -0.066); + offsets[3] = float2(0.05, 0.066); - float3 current_pos = min_pos * (1 - t) + max_pos * t; - //current_pos.x = v_in.pos.x + (t * min_pos.x); - current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); - //current_pos.y = v_in.pos.y + (s * min_pos.y); - //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; + // convert input for vector math + float blur_amount = float(blur) /100; + float glow_amount = float(glow_percent) * 0.1; + float speed = float(pulse_speed_percent) * 0.01; + float luminance_floor = float(min_brightness) * 0.01; + float luminance_ceiling = float(max_brightness) * 0.01; - float2 offset = uv_offset; - offset.x = uv_offset.x * (1 - t + noise); - offset.y = uv_offset.y * (1 - s + noise); + float4 color = image.Sample(textureSampler, v_in.uv); + float4 temp_color = color; + bool glitch_on = glitch; - vert_out.pos = mul(float4(current_pos, 1), ViewProj); - - //float2 scale = uv_scale; - //scale += dot(pos - current_pos, 1); + //circular easing variable + float t = 1 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration - vert_out.uv = v_in.uv * uv_scale + offset; - return vert_out; -} + //if(color.a <= 0.0) color.rgb = float3(0.0,0.0,0.0); + float4 glitch_color = glow_color; -float4 mainImage(VertData v_in) : TARGET -{ - return image.Sample(textureSampler, v_in.uv); -} + for (int n = 0; n < 4; n++){ + //blur sample + b = BlurStyler(t,0,c,d,ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]) ; -technique Draw -{ - pass - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } -} + //test for rand_f color + if (glitch) { + glitch_color = float4(glow_color.rgb * rand_f,glow_color.a); + if ((color.r == rand_f) || (color.g == rand_f) || (color.b == rand_f)) + { + glitch_on = true; + } + } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' -} -switch -regex ($myVerb) { - Get { + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if (((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) || // test luminance + ((color.r == glow_color.r) && (color.g == glow_color.g) && (color.b == glow_color.b)) || //test for chosen color + glitch_on) //test for rand color + { + //glow calc + if (ncolor.a > 0.0 || Apply_To_Alpha_Layer == false) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + //temp_color = max(temp_color,ncolor) * glow_color ;//* ((1-ncolor.a) + color * ncolor.a); + //temp_color += (ncolor * float4(glow_color.rbg, glow_amount)); + + // use temp_color as floor, add glow, use highest alpha of blur pixels, then multiply by glow color + // max is used to simulate addition of vector texture color + temp_color = float4(max(temp_color.rgb, ncolor.rgb * (glow_amount * (b / 2))), // color effected by glow over time + max(temp_color.a, (glow_amount * (b / 2)))) // alpha affected by glow over time + * (glitch_color * (b / 2)); // glow color affected by glow over time + } + } + } + // grab lighter color + return max(color,temp_color); +} + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { $FilterNamePattern = "(?>$( if ($FilterName) { [Regex]::Escape($FilterName) @@ -40569,75 +32047,135 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSShineShader { +function Get-OBSDynamicMaskShader { -[Alias('Set-OBSShineShader','Add-OBSShineShader')] +[Alias('Set-OBSDynamicMaskShader','Add-OBSDynamicMaskShader')] param( -# Set the l_tex of OBSShineShader -[Alias('l_tex')] -[ComponentModel.DefaultBindingProperty('l_tex')] -[String] -$LTex, -# Set the shine_color of OBSShineShader -[Alias('shine_color')] -[ComponentModel.DefaultBindingProperty('shine_color')] -[String] -$ShineColor, -# Set the speed_percent of OBSShineShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the gradient_percent of OBSShineShader -[Alias('gradient_percent')] -[ComponentModel.DefaultBindingProperty('gradient_percent')] -[Int32] -$GradientPercent, -# Set the delay_percent of OBSShineShader -[Alias('delay_percent')] -[ComponentModel.DefaultBindingProperty('delay_percent')] -[Int32] -$DelayPercent, -# Set the Apply_To_Alpha_Layer of OBSShineShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the ease of OBSShineShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the hide of OBSShineShader -[ComponentModel.DefaultBindingProperty('hide')] -[Management.Automation.SwitchParameter] -$Hide, -# Set the reverse of OBSShineShader -[ComponentModel.DefaultBindingProperty('reverse')] -[Management.Automation.SwitchParameter] -$Reverse, -# Set the One_Direction of OBSShineShader -[Alias('One_Direction')] -[ComponentModel.DefaultBindingProperty('One_Direction')] -[Management.Automation.SwitchParameter] -$OneDirection, -# Set the glitch of OBSShineShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSShineShader -[ComponentModel.DefaultBindingProperty('notes')] +# Set the input_source of OBSDynamicMaskShader +[Alias('input_source')] +[ComponentModel.DefaultBindingProperty('input_source')] [String] -$Notes, -# Set the start_adjust of OBSShineShader -[Alias('start_adjust')] -[ComponentModel.DefaultBindingProperty('start_adjust')] +$InputSource, +# Set the red_base_value of OBSDynamicMaskShader +[Alias('red_base_value')] +[ComponentModel.DefaultBindingProperty('red_base_value')] [Single] -$StartAdjust, -# Set the stop_adjust of OBSShineShader -[Alias('stop_adjust')] -[ComponentModel.DefaultBindingProperty('stop_adjust')] +$RedBaseValue, +# Set the red_red_input_value of OBSDynamicMaskShader +[Alias('red_red_input_value')] +[ComponentModel.DefaultBindingProperty('red_red_input_value')] [Single] -$StopAdjust, +$RedRedInputValue, +# Set the red_green_input_value of OBSDynamicMaskShader +[Alias('red_green_input_value')] +[ComponentModel.DefaultBindingProperty('red_green_input_value')] +[Single] +$RedGreenInputValue, +# Set the red_blue_input_value of OBSDynamicMaskShader +[Alias('red_blue_input_value')] +[ComponentModel.DefaultBindingProperty('red_blue_input_value')] +[Single] +$RedBlueInputValue, +# Set the red_alpha_input_value of OBSDynamicMaskShader +[Alias('red_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('red_alpha_input_value')] +[Single] +$RedAlphaInputValue, +# Set the red_multiplier of OBSDynamicMaskShader +[Alias('red_multiplier')] +[ComponentModel.DefaultBindingProperty('red_multiplier')] +[Single] +$RedMultiplier, +# Set the green_base_value of OBSDynamicMaskShader +[Alias('green_base_value')] +[ComponentModel.DefaultBindingProperty('green_base_value')] +[Single] +$GreenBaseValue, +# Set the green_red_input_value of OBSDynamicMaskShader +[Alias('green_red_input_value')] +[ComponentModel.DefaultBindingProperty('green_red_input_value')] +[Single] +$GreenRedInputValue, +# Set the green_green_input_value of OBSDynamicMaskShader +[Alias('green_green_input_value')] +[ComponentModel.DefaultBindingProperty('green_green_input_value')] +[Single] +$GreenGreenInputValue, +# Set the green_blue_input_value of OBSDynamicMaskShader +[Alias('green_blue_input_value')] +[ComponentModel.DefaultBindingProperty('green_blue_input_value')] +[Single] +$GreenBlueInputValue, +# Set the green_alpha_input_value of OBSDynamicMaskShader +[Alias('green_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('green_alpha_input_value')] +[Single] +$GreenAlphaInputValue, +# Set the green_multiplier of OBSDynamicMaskShader +[Alias('green_multiplier')] +[ComponentModel.DefaultBindingProperty('green_multiplier')] +[Single] +$GreenMultiplier, +# Set the blue_base_value of OBSDynamicMaskShader +[Alias('blue_base_value')] +[ComponentModel.DefaultBindingProperty('blue_base_value')] +[Single] +$BlueBaseValue, +# Set the blue_red_input_value of OBSDynamicMaskShader +[Alias('blue_red_input_value')] +[ComponentModel.DefaultBindingProperty('blue_red_input_value')] +[Single] +$BlueRedInputValue, +# Set the blue_green_input_value of OBSDynamicMaskShader +[Alias('blue_green_input_value')] +[ComponentModel.DefaultBindingProperty('blue_green_input_value')] +[Single] +$BlueGreenInputValue, +# Set the blue_blue_input_value of OBSDynamicMaskShader +[Alias('blue_blue_input_value')] +[ComponentModel.DefaultBindingProperty('blue_blue_input_value')] +[Single] +$BlueBlueInputValue, +# Set the blue_alpha_input_value of OBSDynamicMaskShader +[Alias('blue_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('blue_alpha_input_value')] +[Single] +$BlueAlphaInputValue, +# Set the blue_multiplier of OBSDynamicMaskShader +[Alias('blue_multiplier')] +[ComponentModel.DefaultBindingProperty('blue_multiplier')] +[Single] +$BlueMultiplier, +# Set the alpha_base_value of OBSDynamicMaskShader +[Alias('alpha_base_value')] +[ComponentModel.DefaultBindingProperty('alpha_base_value')] +[Single] +$AlphaBaseValue, +# Set the alpha_red_input_value of OBSDynamicMaskShader +[Alias('alpha_red_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_red_input_value')] +[Single] +$AlphaRedInputValue, +# Set the alpha_green_input_value of OBSDynamicMaskShader +[Alias('alpha_green_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_green_input_value')] +[Single] +$AlphaGreenInputValue, +# Set the alpha_blue_input_value of OBSDynamicMaskShader +[Alias('alpha_blue_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_blue_input_value')] +[Single] +$AlphaBlueInputValue, +# Set the alpha_alpha_input_value of OBSDynamicMaskShader +[Alias('alpha_alpha_input_value')] +[ComponentModel.DefaultBindingProperty('alpha_alpha_input_value')] +[Single] +$AlphaAlphaInputValue, +# Set the alpha_multiplier of OBSDynamicMaskShader +[Alias('alpha_multiplier')] +[ComponentModel.DefaultBindingProperty('alpha_multiplier')] +[Single] +$AlphaMultiplier, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -40668,184 +32206,223 @@ $UseShaderTime process { -$shaderName = 'shine' -$ShaderNoun = 'OBSShineShader' +$shaderName = 'dynamic-mask' +$ShaderNoun = 'OBSDynamicMaskShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 -// use color to control shine amount, use transition wipes or make your own alpha texture -// slerp not currently used, for circular effects -//Converted to OpenGL by Exeldro February 14, 2022 -uniform texture2d l_tex; -uniform float4 shine_color ; -uniform int speed_percent< - string label = "speed percent"; +uniform texture2d input_source< + string label = "Input Source"; +>; + +uniform float red_base_value< + string label = "Base Value"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 25; -uniform int gradient_percent< - string label = "gradient percent"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float red_red_input_value< + string label = "Red Input Value"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 20; -uniform int delay_percent< - string label = "delay percent"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_green_input_value< + string label = "Green Input Value"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool ease = false; -uniform bool hide = false; -uniform bool reverse = false; -uniform bool One_Direction = true; -uniform bool glitch = false; -uniform string notes< - string widget_type = "info"; -> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float red_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Red Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -uniform float start_adjust; -uniform float stop_adjust; +uniform float green_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float green_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float green_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Green Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2.0; - if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; - t -= 2.0; - return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; -} +uniform float blue_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float blue_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float blue_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Blue Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0.0,c,d); - return t; -} +uniform float alpha_base_value< + string label = "Base Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; +uniform float alpha_red_input_value< + string label = "Red Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_green_input_value< + string label = "Green Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_blue_input_value< + string label = "Blue Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_alpha_input_value< + string label = "Alpha Input Value"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float alpha_multiplier< + string label = "Multiplier"; + string widget_type = "slider"; + string group = "Alpha Channel"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 1.0; -float4 convert_pmalpha(float4 c) +float4 mainImage(VertData v_in) : TARGET { - float4 ret = c; - if (c.a >= 0.001) - ret.xyz /= c.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; + float4 input_color = input_source.Sample(textureSampler, v_in.uv); + float4 mask; + mask.r = (red_base_value + red_red_input_value * input_color.r + red_green_input_value * input_color.g + red_blue_input_value * input_color.b + red_alpha_input_value * input_color.a) * red_multiplier; + mask.g = (green_base_value + green_red_input_value * input_color.r + green_green_input_value * input_color.g + green_blue_input_value * input_color.b + green_alpha_input_value * input_color.a) * green_multiplier; + mask.b = (blue_base_value + blue_red_input_value * input_color.r + blue_green_input_value * input_color.g + blue_blue_input_value * input_color.b + blue_alpha_input_value * input_color.a) * blue_multiplier; + mask.a = (alpha_base_value + alpha_red_input_value * input_color.r + alpha_green_input_value * input_color.g + alpha_blue_input_value * input_color.b + alpha_alpha_input_value * input_color.a) * alpha_multiplier; + float4 base = image.Sample(textureSampler, v_in.uv); + return base * mask; } -float4 slerp(float4 start, float4 end, float percent) -{ - // Dot product - the cosine of the angle between 2 vectors. - float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; - // Clamp it to be in the range of Acos() - // This may be unnecessary, but floating point - // precision can be a fickle mistress. - dotf = clamp(dotf, -1.0f, 1.0f); - // Acos(dot) returns the angle between start and end, - // And multiplying that by percent returns the angle between - // start and the final result. - float theta = acos(dotf)*percent; - float4 RelativeVec = normalize(end - start * dotf); - - // Orthonormal basis - // The final result. - return ((start*cos(theta)) + (RelativeVec*sin(theta))); -} - -float4 mainImage(VertData v_in) : TARGET -{ - // convert input for vector math - float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); - float speed = speed_percent * 0.01; - float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); - float delay = clamp(delay_percent * 0.01, 0.0, 1.0); - - - // circular easing variable - float direction = abs(sin((elapsed_time - 0.001) * speed)); - float t = abs(sin(elapsed_time * speed)); - - // if time is greater than direction, we are going up! - direction = t - direction; - - // split into segments with frac or mod. - // delay is the gap between starting and ending of the sine wave, use speed to compensate - t = (frac(t) - delay) * (1 / (1 - delay)); - t = 1 + max(t,0.0); - - float s = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); - - //if Unidirectional disable on return - if (One_Direction && (direction < 0.0)) - { - s = 0; - } - else - { - s = Styler(t, 0, c, d, ease); - } - - // combine luma texture and user defined shine color - float luma = l_tex.Sample(textureSampler, v_in.uv).x; - - // - adjust for min and max - if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) - { - - if (reverse) - { - luma = 1.0 - luma; - } - - // user color with luma - float4 output_color = float4(shine_color.rgb, luma); - - float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); - - // use luma texture to add alpha and shine - - // if behind glow, consider trailing gradient shine then show underlying image - if (luma <= time - softness) - { - float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); - if (Apply_To_Alpha_Layer) - alpha_behind *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_behind); - } - - // if in front of glow, consider if the underlying image is hidden - if (luma >= time) - { - // if hide, make the transition better - if (hide) - { - return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); - } - else - { - return rgba; - } - } - - // else show the glow area, with luminance - float alpha_ = (time - luma) / softness; - if (Apply_To_Alpha_Layer) - alpha_ *= rgba.a; - return lerp(rgba, rgba + output_color, alpha_); - } - else - { - return rgba; - } -} - -' +' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 if (-not $myNoun) { @@ -40942,46 +32519,60 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimpleGradientShader { +function Get-OBSEdgeDetectionShader { -[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] +[Alias('Set-OBSEdgeDetectionShader','Add-OBSEdgeDetectionShader')] param( -# Set the speed_percentage of OBSSimpleGradientShader -[Alias('speed_percentage')] -[ComponentModel.DefaultBindingProperty('speed_percentage')] -[Int32] -$SpeedPercentage, -# Set the alpha_percentage of OBSSimpleGradientShader -[Alias('alpha_percentage')] -[ComponentModel.DefaultBindingProperty('alpha_percentage')] -[Int32] -$AlphaPercentage, -# Set the Lens_Flair of OBSSimpleGradientShader -[Alias('Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Lens_Flair')] +# Set the sensitivity of OBSEdgeDetectionShader +[ComponentModel.DefaultBindingProperty('sensitivity')] +[Single] +$Sensitivity, +# Set the invert_edge of OBSEdgeDetectionShader +[Alias('invert_edge')] +[ComponentModel.DefaultBindingProperty('invert_edge')] [Management.Automation.SwitchParameter] -$LensFlair, -# Set the Animate_Lens_Flair of OBSSimpleGradientShader -[Alias('Animate_Lens_Flair')] -[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] +$InvertEdge, +# Set the edge_color of OBSEdgeDetectionShader +[Alias('edge_color')] +[ComponentModel.DefaultBindingProperty('edge_color')] +[String] +$EdgeColor, +# Set the edge_multiply of OBSEdgeDetectionShader +[Alias('edge_multiply')] +[ComponentModel.DefaultBindingProperty('edge_multiply')] [Management.Automation.SwitchParameter] -$AnimateLensFlair, -# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader -[Alias('Apply_To_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +$EdgeMultiply, +# Set the non_edge_color of OBSEdgeDetectionShader +[Alias('non_edge_color')] +[ComponentModel.DefaultBindingProperty('non_edge_color')] +[String] +$NonEdgeColor, +# Set the non_edge_multiply of OBSEdgeDetectionShader +[Alias('non_edge_multiply')] +[ComponentModel.DefaultBindingProperty('non_edge_multiply')] [Management.Automation.SwitchParameter] -$ApplyToAlphaLayer, -# Set the Apply_To_Specific_Color of OBSSimpleGradientShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +$NonEdgeMultiply, +# Set the alpha_channel of OBSEdgeDetectionShader +[Alias('alpha_channel')] +[ComponentModel.DefaultBindingProperty('alpha_channel')] [Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# Set the Color_To_Replace of OBSSimpleGradientShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the notes of OBSSimpleGradientShader +$AlphaChannel, +# Set the alpha_level of OBSEdgeDetectionShader +[Alias('alpha_level')] +[ComponentModel.DefaultBindingProperty('alpha_level')] +[Single] +$AlphaLevel, +# Set the alpha_invert of OBSEdgeDetectionShader +[Alias('alpha_invert')] +[ComponentModel.DefaultBindingProperty('alpha_invert')] +[Management.Automation.SwitchParameter] +$AlphaInvert, +# Set the rand_f of OBSEdgeDetectionShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the notes of OBSEdgeDetectionShader [ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, @@ -41015,126 +32606,102 @@ $UseShaderTime process { -$shaderName = 'simple_gradient' -$ShaderNoun = 'OBSSimpleGradientShader' +$shaderName = 'edge_detection' +$ShaderNoun = 'OBSEdgeDetectionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter - -//lots of room to play here -//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 -uniform int speed_percentage< - string label = "speed percentage"; +// Edge Detection for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform float sensitivity< + string label = "Sensitivity"; string widget_type = "slider"; - int minimum = -500; - int maximum = 500; - int step = 1; -> = 240; // -uniform int alpha_percentage< - string label = "aplha percentage"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.05; +uniform bool invert_edge; +uniform float4 edge_color = {1.0,1.0,1.0,1.0}; +uniform bool edge_multiply; +uniform float4 non_edge_color = {0.0,0.0,0.0,0.0}; +uniform bool non_edge_multiply; +uniform bool alpha_channel; +uniform float alpha_level< + string label = "Alpha level"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 90; -uniform bool Lens_Flair = false; -uniform bool Animate_Lens_Flair = false; -uniform bool Apply_To_Alpha_Layer = false; -uniform bool Apply_To_Specific_Color; -uniform float4 Color_To_Replace; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform bool alpha_invert; +uniform float rand_f; + uniform string notes< string widget_type = "info"; -> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; +> = "''sensitivity'' - 0.01 is max and will create the most edges. Increasing this value decreases the number of edges detected. ''edge non edge color'' - the color to recolor vs the original image. ''edge or non edge multiply'' - multiplies the color against the original color giving it a tint instead of replacing the color. White represents no tint. ''invert edge'' - flips the sensativity and is great for testing and fine tuning. ''alpha channel'' - use an alpha channel to replace original color with transparency. ''alpha_level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 2.00). ''alpha_invert'' - flip what is transparent from darks (default) to lights"; float4 mainImage(VertData v_in) : TARGET { - - float4 background_color = image.Sample(textureSampler, v_in.uv); - int no_colors = 4; - float3 colors[4]; - colors[0] = float3(1.0,0.0,0.0); - colors[1] = float3(0.0,1.0,0.0); - colors[2] = float3(0.0,0.0,1.0); - colors[3] = float3(1.0,1.0,1.0); - float alpha = float(alpha_percentage) * 0.01; - float speed = float(speed_percentage) * 0.01; - - float mx = max(uv_size.x , uv_size.y); - //float2 uv = v_in.uv / mx; - float3 rgb = background_color.rgb; - - // skip if (alpha is zero and only apply to alpha layer is true) - if (!(background_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) - { - rgb = float3(v_in.uv.x, v_in.uv.y, 0.10 + 0.85 * sin(elapsed_time * speed)); - } - - //create lens flare like effect - if (Lens_Flair) - { - float2 lens_flair_coordinates = float2(0.95 ,0.95); - if (Animate_Lens_Flair) - lens_flair_coordinates *= float2(sin(elapsed_time * speed) ,cos(elapsed_time * speed)); - - float dist = distance(v_in.uv, ( lens_flair_coordinates * uv_scale + uv_offset)); - for (int i = 0; i < no_colors; ++i) { - rgb += lerp(rgb, colors[i], dist * 1.5) * 0.25; + float4 c = image.Sample(textureSampler, v_in.uv); + + float s = 3; + float hstep = uv_pixel_interval.x; + float vstep = uv_pixel_interval.y; + + float offsetx = (hstep * s) / 2.0; + float offsety = (vstep * s) / 2.0; + + float4 lum = float4(0.30, 0.59, 0.11, 1 ); + float samples[9]; + + int index = 0; + for(int i = 0; i < s; i++){ + for(int j = 0; j < s; j++){ + samples[index] = dot(image.Sample(textureSampler, float2(v_in.uv.x + (i * hstep) - offsetx, v_in.uv.y + (j * vstep) - offsety )), lum); + index++; } } - - - //float3 col = colors[0]; -/* for (int i = 1; i < no_colors; ++i) { - float3 hole = float3( - sin(1.5 - distance(v_in.uv.x / mx, colors[i].x / mx) * 2.5 * speed), - sin(1.5 - distance(v_in.uv.y / mx, colors[i].y / mx) * 2.5 * speed), - colors[i].z); - rgb = lerp(rgb, hole, 0.1); -*/ -/* float3 hole = lerp(colors[i-1], colors[i], sin(elapsed_time * speed)); - col = lerp(col, hole, v_in.uv.x); -*/ - //} -// rgb = fflerp(rgb, col, 0.5); - - - - //try prepositioned colors with colors[] array timing - //creates an animated color spotlight -/* int color_index = int(sin(elapsed_time * speed) * no_colors); - float3 start_color = colors[color_index]; - float3 end_color; - if (color_index >= 0) - { - end_color = colors[color_index - 1]; + + float vert = samples[2] + samples[8] + (2 * samples[5]) - samples[0] - (2 * samples[3]) - samples[6]; + float hori = samples[6] + (2 * samples[7]) + samples[8] - samples[0] - (2 * samples[1]) - samples[2]; + float4 col; + + float o = ((vert * vert) + (hori * hori)); + bool isEdge = o > sensitivity; + if(invert_edge){ + isEdge = !isEdge; } - else - { - end_color = colors[no_colors - 1]; + if(isEdge) { + col = edge_color; + if(edge_multiply){ + col *= c; + } + } else { + col = non_edge_color; + if(non_edge_multiply){ + col *= c; + } } - rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); -*/ - float4 rgba; - if (Apply_To_Alpha_Layer == false) - { - rgba = lerp(background_color,float4(rgb, 1.0),alpha); + if (alpha_invert) { + lum = 1.0 - lum; } - else - { - rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); + + if(alpha_channel){ + if (edge_multiply && isEdge) { + return clamp(lerp(c, col, alpha_level), 0.0, 1.0); + } + else { + // use max instead of multiply + return clamp(lerp(c, float4(max(c.r, col.r), max(c.g, col.g), max(c.b, col.b), 1.0), alpha_level), 0.0, 1.0); + } + } else { + // col.a = col.a * alpha_level; + return col; } - if (Apply_To_Specific_Color) - { - float4 original_color = background_color; - background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; - rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); - } - return rgba; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -41232,52 +32799,114 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSimplexNoiseShader { +function Get-OBSEmbersShader { -[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] +[Alias('Set-OBSEmbersShader','Add-OBSEmbersShader')] param( -# Set the Snap_Percent of OBSSimplexNoiseShader -[Alias('Snap_Percent')] -[ComponentModel.DefaultBindingProperty('Snap_Percent')] +# Set the ViewProj of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSEmbersShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$SnapPercent, -# Set the Speed_Percent of OBSSimplexNoiseShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] +$ElapsedTime, +# Set the uv_offset of OBSEmbersShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSEmbersShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSEmbersShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSEmbersShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSEmbersShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$SpeedPercent, -# Set the Resolution of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Resolution')] +$RandF, +# Set the rand_instance_f of OBSEmbersShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] [Single] -$Resolution, -# Set the Fractal of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Fractal')] -[Management.Automation.SwitchParameter] -$Fractal, -# Set the Use_Alpha_Layer of OBSSimplexNoiseShader -[Alias('Use_Alpha_Layer')] -[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] -[Management.Automation.SwitchParameter] -$UseAlphaLayer, -# Set the Fore_Color of OBSSimplexNoiseShader -[Alias('Fore_Color')] -[ComponentModel.DefaultBindingProperty('Fore_Color')] -[String] -$ForeColor, -# Set the Back_Color of OBSSimplexNoiseShader -[Alias('Back_Color')] -[ComponentModel.DefaultBindingProperty('Back_Color')] -[String] -$BackColor, -# Set the Alpha_Percent of OBSSimplexNoiseShader -[Alias('Alpha_Percent')] -[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +$RandInstanceF, +# Set the rand_activation_f of OBSEmbersShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] [Single] -$AlphaPercent, -# Set the Notes of OBSSimplexNoiseShader -[ComponentModel.DefaultBindingProperty('Notes')] +$RandActivationF, +# Set the loops of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSEmbersShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('notes')] [String] $Notes, +# Set the Animation_Speed of OBSEmbersShader +[Alias('Animation_Speed')] +[ComponentModel.DefaultBindingProperty('Animation_Speed')] +[Single] +$AnimationSpeed, +# Set the Movement_Direction_Horizontal of OBSEmbersShader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] +[Single] +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSEmbersShader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Movement_Speed_Percent of OBSEmbersShader +[Alias('Movement_Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Movement_Speed_Percent')] +[Int32] +$MovementSpeedPercent, +# Set the Layers_Count of OBSEmbersShader +[Alias('Layers_Count')] +[ComponentModel.DefaultBindingProperty('Layers_Count')] +[Int32] +$LayersCount, +# Set the lumaMin of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSEmbersShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Alpha_Percentage of OBSEmbersShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSEmbersShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41308,205 +32937,372 @@ $UseShaderTime process { -$shaderName = 'simplex_noise' -$ShaderNoun = 'OBSSimplexNoiseShader' +$shaderName = 'embers' +$ShaderNoun = 'OBSEmbersShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 -// for use with obs-shaderfilter 1.0 -//based upon: https://www.shadertoy.com/view/XsX3zB +// Embers effect by Charles Fettinger for obs-shaderfilter plugin 8/2020 v.1 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/wl2Gzc - coverted from and updated + +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +uniform string notes< + string widget_type = "info"; +> = "luma is applied with Apply to Alpha Layer. Movement Speed and Direction can be negatives"; #ifndef OPENGL +#define mat2 float2x2 #define fract frac +#define mix lerp #endif -uniform float Snap_Percent< - string label = "Snap Percent"; +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +uniform float Animation_Speed < + string label = "Animation Speed"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; + float scale = 1.; +> = 1.5; + +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = -100.0; float maximum = 100.0; - float step = 0.01; -> = 7.5; -uniform float Speed_Percent< - string label = "Speed Percent"; + float step = 1.0; +> = 5.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; string widget_type = "slider"; - float minimum = 0.0; + float minimum = -100.0; float maximum = 100.0; + float step = 1.0; +> = 10.0; + +uniform int Movement_Speed_Percent< + string label = "Movement Speed Percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 5; + +uniform int Layers_Count < + string label = "Layers"; + string widget_type = "slider"; + int minimum = 1.0; + int maximum = 100.0; + int step = 1; +> = 15; +/* ps start +*/ + + +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; float step = 0.01; -> = 2.5; -uniform float Resolution< - string label = "Resolution"; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.01; -> = 16.0; -uniform bool Fractal = false; -uniform bool Use_Alpha_Layer = false; -uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; -uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; -uniform float Alpha_Percent< - string label = "Alpha Percent"; +> = 0.01; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; string widget_type = "slider"; float minimum = 0.0; float maximum = 100.0; - float step = 0.01; + float step = 0.1; > = 100.0; -uniform string Notes< - string widget_type = "info"; -> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; +uniform bool Apply_To_Alpha_Layer = true; -float dot(float3 a, float3 b){ - return a.r*b.r+a.g*b.g+a.b*b.b; +#define PI 3.1415927 +#define TWO_PI 6.283185 + +#define PARTICLE_SIZE 0.009 + +#define PARTICLE_SCALE float2(0.5, 1.6) +#define PARTICLE_SCALE_VAR float2(0.25, 0.2) + +#define PARTICLE_BLOOM_SCALE float2(0.5, 0.8) +#define PARTICLE_BLOOM_SCALE_VAR float2(0.3, 0.1) + +#define SPARK_COLOR float3(1.0, 0.4, 0.05) * 1.5 +#define BLOOM_COLOR float3(1.0, 0.4, 0.05) * 0.8 +#define SMOKE_COLOR float3(1.0, 0.43, 0.1) * 0.8 + +#define SIZE_MOD 1.05 +#define ALPHA_MOD 0.9 +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) +#define Movement_Speed Movement_Speed_Percent * 0.01 +#define UV float2(fragCoord.xy / uv_size) + +float hash1_2(float2 x) +{ + return fract(sin(dot(x, float2(52.127, 61.2871))) * 521.582); } -float dot4(float4 a, float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +float2 hash2_2(float2 x) +{ + mat2 m = mat2(20.52, 24.1994, 70.291, 80.171); + float2 y = mul(x, m); + return fract(sin(y) * 492.194); } -float snap(float x, float snap) + +//Simple interpolated noise +float2 noise2_2(float2 uv) { - return snap * round(x / max(0.01,snap)); + //float2 f = fract(uv); + float2 f = smoothstep(0.0, 1.0, fract(uv)); + + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + float2 v00 = hash2_2(uv00); + float2 v01 = hash2_2(uv01); + float2 v10 = hash2_2(uv10); + float2 v11 = hash2_2(uv11); + + float2 v0 = mix(v00, v01, f.y); + float2 v1 = mix(v10, v11, f.y); + float2 v = mix(v0, v1, f.x); + + return v; } -float3 random3(float3 co) +//Simple interpolated noise +float noise1_2(float2 uv) { - float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); - float3 result; - result.z = fract(512.0 * j); - j *= .125; - result.x = fract(512.0 * j); - j *= .125; - result.y = fract(512.0 * j); - return result - 0.5; + float2 f = fract(uv); + + float2 uv00 = floor(uv); + float2 uv01 = uv00 + float2(0, 1); + float2 uv10 = uv00 + float2(1, 0); + float2 uv11 = uv00 + 1.0; + + float v00 = hash1_2(uv00); + float v01 = hash1_2(uv01); + float v10 = hash1_2(uv10); + float v11 = hash1_2(uv11); + + float v0 = mix(v00, v01, f.y); + float v1 = mix(v10, v11, f.y); + float v = mix(v0, v1, f.x); + + return v; } -/* 3d simplex noise */ -float simplex3d(float3 p) { - /* 1. find current tetrahedron T and it''s four vertices */ - /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ - /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ - - /* skew constants for 3d simplex functions */ - float F3 = 0.3333333; - float G3 = 0.1666667; +float layeredNoise1_2(float2 uv, float sizeMod, float alphaMod, int layers, float animation) +{ + float noise = 0.0; + float alpha = 1.0; + float size = 1.0; + float2 offset; + for (int i = 0; i < layers; i++) + { + offset += hash2_2(float2(alpha, size)) * 10.0; + + //Adding noise with movement + noise += noise1_2(uv * size + elapsed_time * animation * 8.0 * Movement_Direction * Movement_Speed + offset) * alpha; + alpha *= alphaMod; + size *= sizeMod; + } + + noise *= (1.0 - alphaMod) / (1.0 - pow(alphaMod, float(layers))); + return noise; +} - /* calculate s and x */ - float3 s = floor(p + dot(p, float3(F3,F3,F3))); - float3 x = p - s + dot(s, float3(G3,G3,G3)); +//Rotates point around 0,0 +float2 rotate(float2 vpoint, float deg) +{ + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + return mul(vpoint, m); +} - /* calculate i1 and i2 */ - float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); - float3 i1 = e * (1.0 - e.zxy); - float3 i2 = 1.0 - e.zxy * (1.0 - e); +//Cell center from point on the grid +float2 voronoiPointFromRoot(float2 root, float deg) +{ + float2 vpoint = hash2_2(root) - 0.5; + float s = sin(deg); + float c = cos(deg); + mat2 m = mat2(s, c, -c, s); + vpoint = mul(vpoint, m) * 0.66; + vpoint += root + 0.5; + return vpoint; +} - /* x1, x2, x3 */ - float3 x1 = x - i1 + G3; - float3 x2 = x - i2 + 2.0 * G3; - float3 x3 = x - 1.0 + 3.0 * G3; +//Voronoi cell point rotation degrees +float degFromRootUV(in float2 uv) +{ + return elapsed_time * Animation_Speed * (hash1_2(uv) - 0.5) * 2.0; +} - /* 2. find four surflets and store them in d */ - float4 w, d; +float2 randomAround2_2(in float2 vpoint, in float2 range, in float2 uv) +{ + return vpoint + (hash2_2(uv) - 0.5) * range; +} - /* calculate surflet weights */ - w.x = dot(x, x); - w.y = dot(x1, x1); - w.z = dot(x2, x2); - w.w = dot(x3, x3); - /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ - w = max(0.61 - w, 0.0); +float3 fireParticles(in float2 uv, in float2 originalUV) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float2 rootUV = floor(uv); + float deg = degFromRootUV(rootUV); + float2 pointUV = voronoiPointFromRoot(rootUV, deg); + float dist = 2.0; + float distBloom = 0.0; + + //UV manipulation for the faster particle movement + float2 tempUV = uv + (noise2_2(uv * 2.0) - 0.5) * 0.1; + tempUV += -(noise2_2(uv * 3.0 + elapsed_time) - 0.5) * 0.07; - /* calculate surflet components */ - d.x = dot(random3(s), x); - d.y = dot(random3(s + i1), x1); - d.z = dot(random3(s + i2), x2); - d.w = dot(random3(s + 1.0), x3); + //Sparks sdf + dist = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_SCALE, PARTICLE_SCALE_VAR, rootUV)); + + //Bloom sdf + distBloom = length(rotate(tempUV - pointUV, 0.7) * randomAround2_2(PARTICLE_BLOOM_SCALE, PARTICLE_BLOOM_SCALE_VAR, rootUV)); - /* multiply d by w^4 */ - w *= w; - w *= w; - d *= w; + //Add sparks + particles += (1.0 - smoothstep(PARTICLE_SIZE * 0.6, PARTICLE_SIZE * 3.0, dist)) * SPARK_COLOR; + + //Add bloom + particles += pow((1.0 - smoothstep(0.0, PARTICLE_SIZE * 6.0, distBloom)) * 1.0, 3.0) * BLOOM_COLOR; - /* 3. return the sum of the four surflets */ - return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); + //Upper disappear curve randomization + float border = (hash1_2(rootUV) - 0.5) * 2.0; + float disappear = 1.0 - smoothstep(border, border + 0.5, originalUV.y); + + //Lower appear curve randomization + border = (hash1_2(rootUV + 0.214) - 1.8) * 0.7; + float appear = smoothstep(border, border + 0.4, originalUV.y); + + return particles * disappear * appear; } -/* directional artifacts can be reduced by rotating each octave */ -float simplex3d_fractal(float3 m3) { - /* const matrices for 3d rotation */ -#ifdef OPENGL - float3x3 rot1 = float3x3( - float3(-0.37, 0.36, 0.85), - float3(-0.14, -0.93, 0.34), - float3(0.92, 0.01, 0.4 )); - float3x3 rot2 = float3x3( - float3(-0.55, -0.39, 0.74), - float3(0.33, -0.91, -0.24), - float3(0.77, 0.12, 0.63 )); - float3x3 rot3 = float3x3( - float3(-0.71, 0.52, -0.47), - float3(-0.08, -0.72, -0.68), - float3(-0.7, -0.45, 0.56 )); +//Layering particles to imitate 3D view +float3 layeredParticles(in float2 uv, in float sizeMod, in float alphaMod, in int layers, in float smoke) +{ + float3 particles = float3(0.0, 0.0, 0.0); + float size = 1.0; + float alpha = 1.0; + float2 offset = float2(0.0, 0.0); + float2 noiseOffset; + float2 bokehUV; + + for (int i = 0; i < layers; i++) + { + //Particle noise movement + noiseOffset = (noise2_2(uv * size * 2.0 + 0.5) - 0.5) * 0.15; + + //UV with applied movement + bokehUV = (uv * size + elapsed_time * Movement_Direction * Movement_Speed) + offset + noiseOffset; + + //Adding particles if there is more smoke, remove smaller particles + particles += fireParticles(bokehUV, uv) * alpha * (1.0 - smoothstep(0.0, 1.0, smoke) * (float(i) / float(layers))); + + //Moving uv origin to avoid generating the same particles + offset += hash2_2(float2(alpha, alpha)) * 10.0; + + alpha *= alphaMod; + size *= sizeMod; + } + + return particles; +} - float3 m = float3(m3.x, m3.y, m3.z); -#else - float3x3 rot1 = { - -0.37, 0.36, 0.85, - -0.14, -0.93, 0.34, - 0.92, 0.01, 0.4 }; - float3x3 rot2 = { - -0.55, -0.39, 0.74, - 0.33, -0.91, -0.24, - 0.77, 0.12, 0.63 }; - float3x3 rot3 = { - -0.71, 0.52, -0.47, - -0.08, -0.72, -0.68, - -0.7, -0.45, 0.56 }; - float3 m = {m3.x, m3.y, m3.z}; -#endif +void mainImage(out float4 fragColor, in float2 fragCoord) +{ + float2 uv = (2.0 * fragCoord - uv_size.xy) / uv_size.x; + float vignette = 1.0 - smoothstep(0.4, 1.4, length(uv + float2(0.0, 0.3))); + + uv *= 1.8; + float alpha = clamp(Alpha_Percentage * .01, 0, 1.0); + + float smokeIntensity = layeredNoise1_2(uv * 10.0 + elapsed_time * 4.0 * Movement_Direction * Movement_Speed, 1.7, 0.7, 6, 0.2); + smokeIntensity *= pow(1.0 - smoothstep(-1.0, 1.6, uv.y), 2.0); + float3 smoke = smokeIntensity * SMOKE_COLOR * 0.8 * vignette; + + //Cutting holes in smoke + smoke *= pow(layeredNoise1_2(uv * 4.0 + elapsed_time * 0.5 * Movement_Direction * Movement_Speed, 1.8, 0.5, 3, 0.2), + 2.0) * 1.5; + + float3 particles = layeredParticles(uv, SIZE_MOD, ALPHA_MOD, Layers_Count, smokeIntensity); + + float4 col = float4(particles + smoke + SMOKE_COLOR * 0.02, alpha); + col.rgb *= vignette; + col.rgb = smoothstep(-0.08, 1.0, col.rgb); + + if (Apply_To_Alpha_Layer) + { + float4 original_color = image.Sample(textureSampler, UV); + + float luma = dot(col.rgb, float3(0.299, 0.587, 0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min, 0.0, 1.0); + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } - return 0.5333333* simplex3d(mul(m, rot1)) - + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) - + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) - + 0.0666667 * simplex3d(8.0 * m); + fragColor = col; } -float4 mainImage(VertData v_in) : TARGET -{ - float time = snap(elapsed_time, Snap_Percent * .01); - float4 rgba = image.Sample(textureSampler, v_in.uv); - float2 p = v_in.uv.xy + float2( 0, -0.5); - float3 p3 = float3(p, time * (Speed_Percent * 0.01)); - - float pixel_alpha = 1.0; - // apply to mainImage rgba - if (Use_Alpha_Layer) { - p3 *= rgba.rgb; - pixel_alpha = rgba.a; - } - - float value; +/*ps end*/ - if (Fractal) { - value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); - } - else { - value = simplex3d(p3 * Resolution); - } +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - //soften color - value = 0.5 + (0.5 * value); - float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} - //use intensity to apply foreground and background colors - float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); - r = lerp(Back_Color, r, saturate(intensity)); - r.a = pixel_alpha; +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} - return lerp(rgba, r, Alpha_Percent * 0.01); +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -41604,22 +33400,38 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSmartDenoiseShader { +function Get-OBSEmbossColorShader { -[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] +[Alias('Set-OBSEmbossColorShader','Add-OBSEmbossColorShader')] param( -# Set the uSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uSigma')] -[Single] -$USigma, -# Set the uKSigma of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uKSigma')] -[Single] -$UKSigma, -# Set the uThreshold of OBSSmartDenoiseShader -[ComponentModel.DefaultBindingProperty('uThreshold')] +# Set the Angle_Steps of OBSEmbossColorShader +[Alias('Angle_Steps')] +[ComponentModel.DefaultBindingProperty('Angle_Steps')] +[Int32] +$AngleSteps, +# Set the Radius_Steps of OBSEmbossColorShader +[Alias('Radius_Steps')] +[ComponentModel.DefaultBindingProperty('Radius_Steps')] +[Int32] +$RadiusSteps, +# Set the ampFactor of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('ampFactor')] [Single] -$UThreshold, +$AmpFactor, +# Set the Up_Down_Percent of OBSEmbossColorShader +[Alias('Up_Down_Percent')] +[ComponentModel.DefaultBindingProperty('Up_Down_Percent')] +[Int32] +$UpDownPercent, +# Set the Apply_To_Alpha_Layer of OBSEmbossColorShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the notes of OBSEmbossColorShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41650,85 +33462,89 @@ $UseShaderTime process { -$shaderName = 'smart_denoise' -$ShaderNoun = 'OBSSmartDenoiseShader' +$shaderName = 'emboss_color' +$ShaderNoun = 'OBSEmbossColorShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) -// Converted to OBS version of HLSL by Euiko on February 10, 2025 - -#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI -#define INV_PI 0.31830988618379067153776752674503 - -uniform float uSigma< - string label = "Sigma"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 3; // max based on the webgl sample, which is 3 - float step = 0.01; -> = 5.0; // default value based on shadertoy -uniform float uKSigma< - string label = "K-Sigma"; +// Color Emboss shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int Angle_Steps< + string label = "Angle Steps"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 24; // max based on the webgl sample, which is 24 - float step = 0.01; -> = 7.0; // the default value is based on the webgl sample -uniform float uThreshold< - string label = "Edge Threshold"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 9; // +uniform int Radius_Steps< + string label = "Radius Steps"; string widget_type = "slider"; - float minimum = 0.01; - float maximum = 2; // max based on the webgl sample, which is 2 + int minimum = 0; + int maximum = 20; + int step = 1; +> = 4; // +uniform float ampFactor< + string label = "amp Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; float step = 0.01; -> = 0.190; // the default value is based on the webgl sample +> = 12.0; +uniform int Up_Down_Percent< + string label = "Up Down Percent"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 0; +uniform bool Apply_To_Alpha_Layer = true; +uniform string notes< + string widget_type = "info"; +> = "Steps limited in range from 0 to 20. Edit shader to remove limits at your own risk."; -// smartDeNoise - parameters -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// float2 uv - actual fragment coord -// float2 size - window size -// float sigma > 0 - sigma Standard Deviation -// float kSigma >= 0 - sigma coefficient -// kSigma * sigma --> radius of the circular kernel -// float threshold - edge sharpening threshold -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default -float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) +float4 mainImage(VertData v_in) : TARGET { - float radius = round(kSigma * sigma); - float radQ = radius * radius; + float radiusSteps = clamp(Radius_Steps, 0, 20); + float angleSteps = clamp(Angle_Steps, 1, 20); + float PI = 3.1415926535897932384626433832795;//acos(-1); + int totalSteps = int(radiusSteps * angleSteps); + float minRadius = (1 * uv_pixel_interval.y); + float maxRadius = (6 * uv_pixel_interval.y); - float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) - float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) + float angleDelta = ((2 * PI) / angleSteps); + float radiusDelta = ((maxRadius - minRadius) / radiusSteps); + float embossAngle = 0.25 * PI; - float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) - float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) + float4 c0 = image.Sample(textureSampler, v_in.uv); + float4 origColor = c0; + float4 accumulatedColor = float4(0,0,0,0); - float4 centrPx = image.Sample(textureSampler, uv); + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) + { + for (int radiusStep = 0; radiusStep < radiusSteps; radiusStep++) { + float radius = minRadius + radiusStep * radiusDelta; - float zBuff = 0.0; - float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); + for (float angle = 0; angle < (2 * PI); angle += angleDelta) { + float2 currentCoord; - float2 d; - for (d.x = -radius; d.x <= radius; d.x += 1.0) - { - float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend - d.y = -pt; - for (; d.y <= pt; d.y += 1.0) - { - float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; - float4 walkPx = image.Sample(textureSampler, uv + (d / size)); - float4 dC = walkPx - centrPx; - float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; - zBuff += deltaFactor; - aBuff += (walkPx * deltaFactor); - } - } - return aBuff / float4(zBuff, zBuff, zBuff, zBuff); -} + float xDiff = radius * cos(angle); + float yDiff = radius * sin(angle); -float4 mainImage(VertData v_in) : TARGET -{ - return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); + currentCoord = v_in.uv + float2(xDiff, yDiff); + float4 currentColor = image.Sample(textureSampler, currentCoord); + float4 colorDiff = abs(c0 - currentColor); + float currentFraction = ((radiusSteps + 1 - radiusStep)) / (radiusSteps + 1); + accumulatedColor += currentFraction * colorDiff / totalSteps * sign(angle - PI);; + + } + } + accumulatedColor *= ampFactor; + + c0 = lerp(c0 + accumulatedColor, c0 - accumulatedColor, (Up_Down_Percent * 0.01)); + } + //return c0 + accumulatedColor; // down; + //return c0 - accumulatedColor; // up + return c0; } ' @@ -41828,47 +33644,20 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecularShineShader { +function Get-OBSEmbossShader { -[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] +[Alias('Set-OBSEmbossShader','Add-OBSEmbossShader')] param( -# Set the Hint of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('Hint')] -[String] -$Hint, -# Set the roughness of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('roughness')] -[Single] -$Roughness, -# Set the lightStrength of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('lightStrength')] -[Single] -$LightStrength, -# Set the LightPositionX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionX')] -[Single] -$LightPositionX, -# Set the LightPositionY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('LightPositionY')] -[Single] -$LightPositionY, -# Set the flattenNormal of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('flattenNormal')] -[Single] -$FlattenNormal, -# Set the stretchNormalX of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalX')] -[Single] -$StretchNormalX, -# Set the stretchNormalY of OBSSpecularShineShader -[ComponentModel.DefaultBindingProperty('stretchNormalY')] -[Single] -$StretchNormalY, -# Set the Light_Color of OBSSpecularShineShader -[Alias('Light_Color')] -[ComponentModel.DefaultBindingProperty('Light_Color')] -[Single[]] -$LightColor, +# Set the Use_Color of OBSEmbossShader +[Alias('Use_Color')] +[ComponentModel.DefaultBindingProperty('Use_Color')] +[Management.Automation.SwitchParameter] +$UseColor, +# Set the Apply_To_Alpha_Layer of OBSEmbossShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -41899,108 +33688,42 @@ $UseShaderTime process { -$shaderName = 'specular-shine' -$ShaderNoun = 'OBSSpecularShineShader' +$shaderName = 'emboss' +$ShaderNoun = 'OBSEmbossShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft - -uniform string Hint< - string widget_type = "info"; -> = "Try using a black color source with the additive blend mode!"; - -uniform float roughness< - string label = "Roughness"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.25; - -uniform float lightStrength< - string label = "lightStrength"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.001; -> = 0.5; - -uniform float LightPositionX< - string label = "Light Position X"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float LightPositionY< - string label = "Light Position Y"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.001; -> = 0.0; - -uniform float flattenNormal< - string label = "Flatten Normal"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.1; - -uniform float stretchNormalX< - string label = "Stretch Normal X"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; - -uniform float stretchNormalY< - string label = "Stretch Normal Y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 4.0; - float step = 0.01; -> = 1; - -uniform float3 Light_Color = {1,1,1}; - -float Square(float a) { return a * a; } - -float CookTorrance(float3 lightDir, float3 normal, float roughness) { - float3 h = normalize(lightDir + float3(0,0,1)); - float nh2 = Square(saturate(dot(normal, h))); - float lh2 = Square(saturate(dot(lightDir, h))); - float r2 = Square(roughness); - float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); - float normalization = roughness * 4.0 + 2.0; - return r2 / (d2 * max(0.1, lh2) * normalization); -} - -#define PI 3.14159265 +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 +uniform bool Use_Color; +uniform bool Apply_To_Alpha_Layer = true; float4 mainImage(VertData v_in) : TARGET { - float4 c0 = image.Sample(textureSampler, v_in.uv); - - float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); - - float2 normalUV = v_in.uv - 0.5; - normalUV.x /= stretchNormalX; - normalUV.y /= stretchNormalY; - normalUV += 0.5; - - float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); - normal.z *= -1; + float dx = 1 / uv_size.x; + float dy = 1 / uv_size.y; - normal = lerp(normal, float3(0,0,-1), flattenNormal); + float4 c0 = image.Sample(textureSampler, v_in.uv); + if (c0.a > 0.0 || Apply_To_Alpha_Layer == false) + { + float4 c1 = image.Sample(textureSampler, v_in.uv + float2(-dx, -dy)); + float4 c2 = image.Sample(textureSampler, v_in.uv + float2(0, -dy)); + float4 c4 = image.Sample(textureSampler, v_in.uv + float2(-dx, 0)); + float4 c6 = image.Sample(textureSampler, v_in.uv + float2(dx, 0)); + float4 c8 = image.Sample(textureSampler, v_in.uv + float2(0, dy)); + float4 c9 = image.Sample(textureSampler, v_in.uv + float2(dx, dy)); - float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; + c0 = (-c1 - c2 - c4 + c6 + c8 + c9); + float c = (c0.r + c0.g + c0.b) / 3 + 0.5; + c0 = float4(c,c,c,c); - return float4(c0 + light,c0.a); + if (Use_Color) + { + float4 rgba = image.Sample(textureSampler, v_in.uv); + return (0.5 * rgba) + c0; + } + } + return c0; } ' @@ -42100,43 +33823,60 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpotlightShader { +function Get-OBSExeldroBentCameraShader { -[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] +[Alias('Set-OBSExeldroBentCameraShader','Add-OBSExeldroBentCameraShader')] param( -# Set the Speed_Percent of OBSSpotlightShader -[Alias('Speed_Percent')] -[ComponentModel.DefaultBindingProperty('Speed_Percent')] +# Set the left_side_width of OBSExeldroBentCameraShader +[Alias('left_side_width')] +[ComponentModel.DefaultBindingProperty('left_side_width')] [Single] -$SpeedPercent, -# Set the Focus_Percent of OBSSpotlightShader -[Alias('Focus_Percent')] -[ComponentModel.DefaultBindingProperty('Focus_Percent')] +$LeftSideWidth, +# Set the left_side_size of OBSExeldroBentCameraShader +[Alias('left_side_size')] +[ComponentModel.DefaultBindingProperty('left_side_size')] [Single] -$FocusPercent, -# Set the Glitch of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the Spotlight_Color of OBSSpotlightShader -[Alias('Spotlight_Color')] -[ComponentModel.DefaultBindingProperty('Spotlight_Color')] -[String] -$SpotlightColor, -# Set the Horizontal_Offset of OBSSpotlightShader -[Alias('Horizontal_Offset')] -[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] +$LeftSideSize, +# Set the left_side_shadow of OBSExeldroBentCameraShader +[Alias('left_side_shadow')] +[ComponentModel.DefaultBindingProperty('left_side_shadow')] [Single] -$HorizontalOffset, -# Set the Vertical_Offset of OBSSpotlightShader -[Alias('Vertical_Offset')] -[ComponentModel.DefaultBindingProperty('Vertical_Offset')] +$LeftSideShadow, +# Set the left_flip_width of OBSExeldroBentCameraShader +[Alias('left_flip_width')] +[ComponentModel.DefaultBindingProperty('left_flip_width')] [Single] -$VerticalOffset, -# Set the Notes of OBSSpotlightShader -[ComponentModel.DefaultBindingProperty('Notes')] -[String] -$Notes, +$LeftFlipWidth, +# Set the left_flip_shadow of OBSExeldroBentCameraShader +[Alias('left_flip_shadow')] +[ComponentModel.DefaultBindingProperty('left_flip_shadow')] +[Single] +$LeftFlipShadow, +# Set the right_side_width of OBSExeldroBentCameraShader +[Alias('right_side_width')] +[ComponentModel.DefaultBindingProperty('right_side_width')] +[Single] +$RightSideWidth, +# Set the right_side_size of OBSExeldroBentCameraShader +[Alias('right_side_size')] +[ComponentModel.DefaultBindingProperty('right_side_size')] +[Single] +$RightSideSize, +# Set the right_side_shadow of OBSExeldroBentCameraShader +[Alias('right_side_shadow')] +[ComponentModel.DefaultBindingProperty('right_side_shadow')] +[Single] +$RightSideShadow, +# Set the right_flip_width of OBSExeldroBentCameraShader +[Alias('right_flip_width')] +[ComponentModel.DefaultBindingProperty('right_flip_width')] +[Single] +$RightFlipWidth, +# Set the right_flip_shadow of OBSExeldroBentCameraShader +[Alias('right_flip_shadow')] +[ComponentModel.DefaultBindingProperty('right_flip_shadow')] +[Single] +$RightFlipShadow, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42167,62 +33907,126 @@ $UseShaderTime process { -$shaderName = 'spotlight' -$ShaderNoun = 'OBSSpotlightShader' +$shaderName = 'exeldro-bent-camera' +$ShaderNoun = 'OBSExeldroBentCameraShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 -uniform float Speed_Percent< - string label = "Speed Percent"; +uniform float left_side_width< + string label = "Left side width"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.01; -> = 100.0; -uniform float Focus_Percent< - string label = "Focus Percent"; +> = 0.1; +uniform float left_side_size< + string label = "Left side size"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 100.0; + float maximum = 1.0; float step = 0.01; -> = 15.0; -uniform bool Glitch; -uniform float4 Spotlight_Color; -uniform float Horizontal_Offset< - string label = "Horizontal Offset"; +> = 0.9; +uniform float left_side_shadow< + string label = "Left side shadow"; string widget_type = "slider"; - float minimum = -1.0; + float minimum = 0.0; float maximum = 1.0; - float step = 0.001; -> = 0.0; -uniform float Vertical_Offset< - string label = "Vertical Offset"; + float step = 0.01; +> = 0.8; +uniform float left_flip_width< + string label = "Left flip width"; string widget_type = "slider"; - float minimum = -1.0; + float minimum = 0.0; float maximum = 1.0; - float step = 0.001; -> = -0.5; -uniform string Notes< - string widget_type = "info"; -> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; + float step = 0.01; +> = 0.05; +uniform float left_flip_shadow< + string label = "Left flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; + +uniform float right_side_width< + string label = "Right side width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; +uniform float right_side_size< + string label = "Right side size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform float right_side_shadow< + string label = "Right side shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.8; +uniform float right_flip_width< + string label = "Right flip width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; +uniform float right_flip_shadow< + string label = "Right flip shadow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; float4 mainImage(VertData v_in) : TARGET { - float speed = Speed_Percent * 0.01; - float focus = Focus_Percent; - if (Glitch) - { - speed *= ((rand_f * 2) - 1) * 0.01; - focus *= ((rand_f * 1.1) - 0.1); - } - - float PI = 3.1415926535897932384626433832795;//acos(-1); - float4 c0 = image.Sample( textureSampler, v_in.uv); - float3 lightsrc = float3(sin(elapsed_time * speed * PI * 0.667) *.5 + .5 + Horizontal_Offset, cos(elapsed_time * speed * PI) *.5 + .5 + Vertical_Offset, 1); - float3 light = normalize(lightsrc - float3( v_in.uv.x + (Horizontal_Offset * speed), v_in.uv.y + (Vertical_Offset * speed), 0)); - c0 *= pow(dot(light, float3(0, 0, 1)), focus) * Spotlight_Color; + float2 pos=v_in.uv; + float shadow = 1.0; + if(pos.x < left_side_width){ + pos.y -= 0.5; + pos.y /= left_side_size; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width / 2.0; + pos.x /= left_side_size; + pos.x += left_side_width + left_flip_width / 2.0; + shadow = left_side_shadow; + }else if(pos.x < left_side_width + left_flip_width){ + float factor = 1.0 - ((left_side_width + left_flip_width)-pos.x)/left_flip_width*(1.0 - left_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= left_side_width + left_flip_width; + pos.x /= factor; + pos.x += left_side_width + left_flip_width; + shadow = left_flip_shadow; + } - return c0; + if(1.0 - pos.x < right_side_width){ + pos.y -= 0.5; + pos.y /= right_side_size; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width / 2.0); + pos.x /= right_side_size; + pos.x += 1.0 - (right_side_width + right_flip_width / 2.0); + shadow = right_side_shadow; + }else if(1.0 - pos.x < right_side_width + right_flip_width){ + float factor = 1.0 - ((right_side_width + right_flip_width) - (1.0 - pos.x))/right_flip_width*(1.0 - right_side_size); + pos.y -= 0.5; + pos.y /= factor; + pos.y += 0.5; + pos.x -= 1.0 - (right_side_width + right_flip_width); + pos.x /= factor; + pos.x += 1.0 -(right_side_width + right_flip_width); + shadow = right_flip_shadow; + } + float4 p_color = image.Sample(textureSampler, pos); + p_color.rgb *= shadow; + return p_color; } ' } @@ -42321,40 +34125,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSwirlShader { +function Get-OBSFadeTransitionShader { -[Alias('Set-OBSSwirlShader','Add-OBSSwirlShader')] +[Alias('Set-OBSFadeTransitionShader','Add-OBSFadeTransitionShader')] param( -# Set the radius of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('radius')] +# Set the image_a of OBSFadeTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSFadeTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSFadeTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] [Single] -$Radius, -# Set the angle of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the center_x of OBSSwirlShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] -[Single] -$CenterX, -# Set the center_y of OBSSwirlShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] -[Single] -$CenterY, -# Set the animate of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('animate')] -[Management.Automation.SwitchParameter] -$Animate, -# Set the inverse of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('inverse')] +$TransitionTime, +# Set the convert_linear of OBSFadeTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] [Management.Automation.SwitchParameter] -$Inverse, -# Set the notes of OBSSwirlShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$ConvertLinear, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42385,85 +34179,30 @@ $UseShaderTime process { -$shaderName = 'Swirl' -$ShaderNoun = 'OBSSwirlShader' +$shaderName = 'fade-transition' +$ShaderNoun = 'OBSFadeTransitionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "Radius"; +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; string widget_type = "slider"; float minimum = 0.0; float maximum = 1.0; float step = 0.001; -> = 0.5; // -uniform float angle< - string label = "Angle"; - string widget_type = "slider"; - float minimum = -360.0; - float maximum = 360.0; - float step = 0.01; -> = 270.0; // - -uniform float center_x< - string label = "Center x"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // -uniform float center_y< - string label = "Center y"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; // - -uniform bool animate = false; -uniform bool inverse = false; - -uniform string notes< - string widget_type = "info"; -> = "Distorts the screen, twisting the image in a circular motion." +> = 0.5; +uniform bool convert_linear = true; float4 mainImage(VertData v_in) : TARGET { - - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; - - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float percent = (radius-dist)/(radius); - percent = inverse == false ? percent : 1 - percent; - - float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; - - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv ); - } - + float4 a_val = image_a.Sample(textureSampler, v_in.uv); + float4 b_val = image_b.Sample(textureSampler, v_in.uv); + float4 rgba = lerp(a_val, b_val, transition_time); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -42561,82 +34300,34 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTetraShader { +function Get-OBSFillColorGradientShader { -[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] +[Alias('Set-OBSFillColorGradientShader','Add-OBSFillColorGradientShader')] param( -# Set the redR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redR')] -[Single] -$RedR, -# Set the redG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redG')] -[Single] -$RedG, -# Set the redB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('redB')] -[Single] -$RedB, -# Set the yelR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelR')] -[Single] -$YelR, -# Set the yelG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelG')] -[Single] -$YelG, -# Set the yelB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('yelB')] -[Single] -$YelB, -# Set the grnR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnR')] -[Single] -$GrnR, -# Set the grnG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnG')] -[Single] -$GrnG, -# Set the grnB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('grnB')] -[Single] -$GrnB, -# Set the cynR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynR')] -[Single] -$CynR, -# Set the cynG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynG')] -[Single] -$CynG, -# Set the cynB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('cynB')] -[Single] -$CynB, -# Set the bluR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluR')] -[Single] -$BluR, -# Set the bluG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluG')] -[Single] -$BluG, -# Set the bluB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('bluB')] -[Single] -$BluB, -# Set the magR of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magR')] +# Set the Fill of OBSFillColorGradientShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$MagR, -# Set the magG of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magG')] +$Fill, +# Set the Gradient_Width of OBSFillColorGradientShader +[Alias('Gradient_Width')] +[ComponentModel.DefaultBindingProperty('Gradient_Width')] [Single] -$MagG, -# Set the magB of OBSTetraShader -[ComponentModel.DefaultBindingProperty('magB')] +$GradientWidth, +# Set the Gradient_Offset of OBSFillColorGradientShader +[Alias('Gradient_Offset')] +[ComponentModel.DefaultBindingProperty('Gradient_Offset')] [Single] -$MagB, +$GradientOffset, +# Set the Fill_Direction of OBSFillColorGradientShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill_Color of OBSFillColorGradientShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -42667,203 +34358,81 @@ $UseShaderTime process { -$shaderName = 'tetra' -$ShaderNoun = 'OBSTetraShader' +$shaderName = 'fill_color_gradient' +$ShaderNoun = 'OBSFillColorGradientShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Tetrahedral Interpolation Shader for OBS - -uniform float redR< - string label = "Red in Red"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float redG< - string label = "Green in Red"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float redB< - string label = "Blue in Red"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float yelR< - string label = "Red in Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float yelG< - string label = "Green in Yellow"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float yelB< - string label = "Blue in Yellow"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float grnR< - string label = "Red in Green"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float grnG< - string label = "Green in Green"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float grnB< - string label = "Blue in Green"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float cynR< - string label = "Red in Cyan"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; - -uniform float cynG< - string label = "Green in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float cynB< - string label = "Blue in Cyan"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; - -uniform float bluR< - string label = "Red in Blue"; +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; + float minimum = 0; + float maximum = 1; + float step = 0.005; +> = 0.500; -uniform float bluG< - string label = "Green in Blue"; +uniform float Gradient_Width< + string label = "Gradient Width"; string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; + float minimum = 0; + float maximum = 0.15; // Adjust the maximum value as needed float step = 0.01; -> = 0.0; +> = 0.05; -uniform float bluB< - string label = "Blue in Blue"; +uniform float Gradient_Offset< + string label = "Gradient Offset"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; + float minimum = 0; + float maximum = 0.100; // Adjust the maximum value as needed + float step = 0.005; +> = 0.00; -uniform float magR< - string label = "Red in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +uniform int Fill_Direction< + string label = "Fill from:"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Left"; + int option_1_value = 1; + string option_1_label = "Right"; + int option_2_value = 2; + string option_2_label = "Bottom"; + int option_3_value = 3; + string option_3_label = "Top"; +> = 0; -uniform float magG< - string label = "Green in Magenta"; - string widget_type = "slider"; - float minimum = -1.0; - float maximum = 1.0; - float step = 0.01; -> = 0.0; +uniform float4 Fill_Color; -uniform float magB< - string label = "Blue in Magenta"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 2.0; - float step = 0.01; -> = 1.0; +float4 mainImage(VertData v_in) : TARGET +{ + float distanceToEdge = 0.0; + // Calculate distance to the fill edge based on the selected direction + if (Fill_Direction == 0) + distanceToEdge = Fill - v_in.uv.x; + else if (Fill_Direction == 1) + distanceToEdge = v_in.uv.x - (1.0 - Fill); + else if (Fill_Direction == 2) + distanceToEdge = v_in.uv.y - (1.0 - Fill); + else if (Fill_Direction == 3) + distanceToEdge = Fill - v_in.uv.y; -float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { - float r = RGBimage.x; - float g = RGBimage.y; - float b = RGBimage.z; + // Calculate the gradient factor based on the distance to the edge and the gradient width + float gradientOffset = (Fill == 0.0) ? 0.0 : (Fill == 1.0 ? 0.0 : Gradient_Offset); + float gradientWidth = (Fill == 0.0 || Fill == 1.0) ? 0.0 : Gradient_Width; - float3 wht = float3(1.0, 1.0, 1.0); + // Adjust distanceToEdge by the Gradient_Offset + distanceToEdge += gradientOffset; - if (r > g) { - if (g > b) { - // r > g > b - return r * red + g * (yel - red) + b * (wht - yel); - } else if (r > b) { - // r > b > g - return r * red + g * (wht - mag) + b * (mag - red); - } else { - // b > r > g - return r * (mag - blu) + g * (wht - mag) + b * blu; - } - } else { - if (b > g) { - // b > g > r - return r * (wht - cyn) + g * (cyn - blu) + b * blu; - } else if (b > r) { - // g > b > r - return r * (wht - cyn) + g * grn + b * (cyn - grn); - } else { - // g > r > b - return r * (yel - grn) + g * grn + b * (wht - yel); - } - } -} + // Normalize the distance to be between 0 and 1 + distanceToEdge = saturate(distanceToEdge); -float4 mainImage(VertData v_in) : TARGET -{ - float4 inputColor = image.Sample(textureSampler, v_in.uv); - float alpha = inputColor.a; + // float gradientWidth = Fill < 1.0 ? Gradient_Width : Gradient_Width * (1.0 - Fill); + // float gradientFactor = smoothstep(0.0, gradientWidth, distanceToEdge); + float gradientFactor = clamp(distanceToEdge / gradientWidth, 0.0, 1.0); - float3 red = float3(redR, redG, redB); - float3 yel = float3(yelR, yelG, yelB); - float3 grn = float3(grnR, grnG, grnB); - float3 cyn = float3(cynR, cynG, cynB); - float3 blu = float3(bluR, bluG, bluB); - float3 mag = float3(magR, magG, magB); + // Blend between the fill color and the original image color using the gradient factor + float4 finalColor = lerp(image.Sample(textureSampler, v_in.uv), Fill_Color, gradientFactor); - float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); - return float4(outputColor, alpha); + return finalColor; } ' @@ -42963,14 +34532,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSThermalShader { +function Get-OBSFillColorLinearShader { -[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] +[Alias('Set-OBSFillColorLinearShader','Add-OBSFillColorLinearShader')] param( -# Set the strength of OBSThermalShader -[ComponentModel.DefaultBindingProperty('strength')] +# Set the Fill of OBSFillColorLinearShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$Strength, +$Fill, +# Set the Fill_Direction of OBSFillColorLinearShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] +[Int32] +$FillDirection, +# Set the Fill_Color of OBSFillColorLinearShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43001,48 +34580,57 @@ $UseShaderTime process { -$shaderName = 'thermal' -$ShaderNoun = 'OBSThermalShader' +$shaderName = 'fill_color_linear' +$ShaderNoun = 'OBSFillColorLinearShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/mdKXzG - -uniform float strength< - string label = "Strength"; +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 200.0; - float step = 0.1; -> = 100.0; - -float greyScale(float3 c) { - return 0.29 * c.r + 0.60 * c.g + 0.11; -} + float minimum = 0; + float maximum = 1; + float step = 0.005; +>; +uniform int Fill_Direction< + string label = "Fill from:"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Left"; + int option_1_value = 1; + string option_1_label = "Right"; + int option_2_value = 2; + string option_2_label = "Top"; + int option_3_value = 3; + string option_3_label = "Bottom"; +> = 0; +uniform float4 Fill_Color; -float3 heatMap(float greyValue) { - float3 heat; - heat.r = smoothstep(0.5, 0.8, greyValue); - if(greyValue >= 0.8333) { - heat.r *= (1.1 - greyValue) * 5.0; +float4 mainImage(VertData v_in) : TARGET +{ + bool is_inside_fill = true; + + // Check if the pixel is within the specified "fill width" on the left side + if(Fill_Direction == 0){ + is_inside_fill = v_in.uv.x > Fill; } - if(greyValue > 0.6) { - heat.g = smoothstep(1.0, 0.7, greyValue); - } else { - heat.g = smoothstep(0.0, 0.7, greyValue); - } - heat.b = smoothstep(1.0, 0.0, greyValue); - if(greyValue <= 0.3333) { - heat.b *= greyValue / 0.3; + if(Fill_Direction == 1) + { + is_inside_fill = v_in.uv.x < (1.0 - Fill); } - return heat; -} - -float4 mainImage(VertData v_in) : TARGET -{ - float4 c = image.Sample(textureSampler, v_in.uv); - float greyValue = greyScale(c.rgb); - float3 h = heatMap(greyValue*(strength/100.0)); - return float4(h.r, h.g, h.b, c.a); + if(Fill_Direction == 2) + { + is_inside_fill = v_in.uv.y > Fill; + } + if(Fill_Direction == 3) + { + is_inside_fill = v_in.uv.y < (1.0 - Fill); + } + + // Invert is_inside_fill + is_inside_fill = !is_inside_fill; + + // If inside the "fill," make the pixel selected colour; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } ' } @@ -43141,26 +34729,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTvCrtSubpixelShader { +function Get-OBSFillColorRadialDegreesShader { -[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] +[Alias('Set-OBSFillColorRadialDegreesShader','Add-OBSFillColorRadialDegreesShader')] param( -# Set the channelWidth of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelWidth')] -[Int32] -$ChannelWidth, -# Set the channelHeight of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('channelHeight')] -[Int32] -$ChannelHeight, -# Set the hGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('hGap')] -[Int32] -$HGap, -# Set the vGap of OBSTvCrtSubpixelShader -[ComponentModel.DefaultBindingProperty('vGap')] +# Set the Fill_Direction of OBSFillColorRadialDegreesShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] [Int32] -$VGap, +$FillDirection, +# Set the Fill of OBSFillColorRadialDegreesShader +[ComponentModel.DefaultBindingProperty('Fill')] +[Single] +$Fill, +# Set the Start_Angle of OBSFillColorRadialDegreesShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] +[Single] +$StartAngle, +# Set the Offset_X of OBSFillColorRadialDegreesShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialDegreesShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialDegreesShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43191,80 +34792,97 @@ $UseShaderTime process { -$shaderName = 'tv-crt-subpixel' -$ShaderNoun = 'OBSTvCrtSubpixelShader' +$shaderName = 'fill_color_radial_degrees' +$ShaderNoun = 'OBSFillColorRadialDegreesShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro +#define PI 3.141592653589793238 -// width of a single color channel in pixels -uniform int channelWidth< - string label = "Channel Width"; +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; + +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; + float minimum = 0; + float maximum = 360; + float step = 1.00000; +>; -// height of color channels in pixels -uniform int channelHeight< - string label = "Channel Height"; +uniform float Start_Angle< + string label = "Start Angle"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 3; + float minimum = 0; + float maximum = 720; + float step = 1.00000; +> = 360.0; -// horizontal distance between two neighboring pixels -uniform int hGap< - string label = "Horizontal Gap"; +uniform float Offset_X< + string label = "Offset X"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -// vertical distance between two neighboring pixels -uniform int vGap< - string label = "Vertical Gap"; +uniform float Offset_Y< + string label = "Offset Y"; string widget_type = "slider"; - int minimum = 1; - int maximum = 20; - int step = 1; -> = 1; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float4 Fill_Color; float4 mainImage(VertData v_in) : TARGET { - float columns = float(channelWidth * 3 + hGap); - float pixelHeight = float(channelHeight + vGap); + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); - float2 fragCoord = v_in.uv * uv_size; - float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); - float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); - float2 sampleUv = pixel / sampleRes; + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); - // color of sample point - float4 col = image.Sample(textureSampler, sampleUv); - - int column = int(fragCoord.x) % (channelWidth * 3 + hGap); + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; - // set color based on which channel this fragment corresponds to - if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); - else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); - else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); - else col = float4(0.0, 0.0, 0.0, col.a); + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); - // offset every other column of pixels - int height = int(pixelHeight); - if (int(pixel.x) % 2 == 0) { - if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); - } else { - if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); + // Convert angle from radians to degrees + angle = degrees(angle); + + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; + + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; } - // Output to screen - return col; + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; + + // Check if the angle is within the specified "fill width" + bool is_inside_fill = angle < Fill; + + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -43362,28 +34980,39 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwistShader { +function Get-OBSFillColorRadialPercentageShader { -[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] +[Alias('Set-OBSFillColorRadialPercentageShader','Add-OBSFillColorRadialPercentageShader')] param( -# Set the center_x_percent of OBSTwistShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSTwistShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] +# Set the Fill_Direction of OBSFillColorRadialPercentageShader +[Alias('Fill_Direction')] +[ComponentModel.DefaultBindingProperty('Fill_Direction')] [Int32] -$CenterYPercent, -# Set the power of OBSTwistShader -[ComponentModel.DefaultBindingProperty('power')] +$FillDirection, +# Set the Fill of OBSFillColorRadialPercentageShader +[ComponentModel.DefaultBindingProperty('Fill')] [Single] -$Power, -# Set the rotation of OBSTwistShader -[ComponentModel.DefaultBindingProperty('rotation')] +$Fill, +# Set the Start_Angle of OBSFillColorRadialPercentageShader +[Alias('Start_Angle')] +[ComponentModel.DefaultBindingProperty('Start_Angle')] [Single] -$Rotation, +$StartAngle, +# Set the Offset_X of OBSFillColorRadialPercentageShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSFillColorRadialPercentageShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the Fill_Color of OBSFillColorRadialPercentageShader +[Alias('Fill_Color')] +[ComponentModel.DefaultBindingProperty('Fill_Color')] +[String] +$FillColor, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43414,59 +35043,98 @@ $UseShaderTime process { -$shaderName = 'twist' -$ShaderNoun = 'OBSTwistShader' +$shaderName = 'fill_color_radial_percentage' +$ShaderNoun = 'OBSFillColorRadialPercentageShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percentage"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float power< - string label = "power"; +#define PI 3.141592653589793238 + +uniform int Fill_Direction< + string label = "Fill Direction"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Clockwise"; + int option_1_value = 1; + string option_1_label = "Counter-Clockwise"; +> = 0; + +uniform float Fill< + string label = "Fill"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.3; -uniform float rotation< - string label = "rotation"; + float maximum = 1.0; + float step = 0.00005; +> = 0.0; + +uniform float Start_Angle< + string label = "Start Angle"; string widget_type = "slider"; - float minimum = -100.0; - float maximum = 100.0; - float step = 0.001; -> = 2.0; + float minimum = 0; + float maximum = 720; + float step = 1.00000; +> = 360.0; -#ifndef OPENGL -#define mat2 float2x2 -#endif +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -mat2 rotate(float angle){ - return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); -} +uniform float Offset_Y< + string label = "Offset Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; + +uniform float4 Fill_Color; float4 mainImage(VertData v_in) : TARGET { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float d = distance(center_pos,v_in.uv); - if(d > power){ - return image.Sample(textureSampler, v_in.uv); + // Calculate the center of the screen based on aspect ratio + float aspectRatioX = uv_size.x / uv_size.y; + float2 center = float2(0.5 * aspectRatioX + Offset_X, 0.5 + Offset_Y); + + // Normalize the UV coordinates based on aspect ratio + float2 normalizedUV = v_in.uv * float2(aspectRatioX, 1.0); + + // Calculate the direction vector from the center to the current pixel + float2 dir = normalizedUV - center; + + // Calculate the angle in radians + float angle = atan2(dir.y, dir.x); + + // Convert angle from radians to degrees + angle = degrees(angle); + + // Offset the angle to start from the specified starting angle + angle += Start_Angle + 90.0; // Subtract 90 degrees to start at 12 o''clock + if (angle >= 360.0) + angle -= 360.0; + + // Adjust the angle based on the selected fill direction + if (Fill_Direction == 1) { + // Counter-clockwise fill + angle = 360.0 - angle; } - float r = (cos(d*3.14159265359/power) +1)/2 * rotation; - float2 pos = v_in.uv - center_pos; - pos = mul(pos, rotate(r)); - pos += center_pos; - return image.Sample(textureSampler, pos); + + // Ensure angle is within [0, 360] range + if (angle < 0.0) + angle += 360.0; + else if (angle >= 360.0) + angle -= 360.0; + + // Calculate the percentage of the angle + float anglePercentage = angle / 360.0; + + // Check if the angle percentage is within the specified "fill percentage" + bool is_inside_fill = anglePercentage < Fill; + + // If inside the "fill," make the pixel selected color; otherwise, use the original image color + return is_inside_fill ? Fill_Color : image.Sample(textureSampler, v_in.uv); } ' @@ -43566,73 +35234,71 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSTwoPassDropShadowShader { +function Get-OBSFilterTemplateShader { -[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] +[Alias('Set-OBSFilterTemplateShader','Add-OBSFilterTemplateShader')] param( -# Set the ViewProj of OBSTwoPassDropShadowShader +# Set the ViewProj of OBSFilterTemplateShader [ComponentModel.DefaultBindingProperty('ViewProj')] [Single[][]] $ViewProj, -# Set the image of OBSTwoPassDropShadowShader +# Set the image of OBSFilterTemplateShader [ComponentModel.DefaultBindingProperty('image')] [String] $Image, -# Set the elapsed_time of OBSTwoPassDropShadowShader +# Set the elapsed_time of OBSFilterTemplateShader [Alias('elapsed_time')] [ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] $ElapsedTime, -# Set the uv_offset of OBSTwoPassDropShadowShader +# Set the uv_offset of OBSFilterTemplateShader [Alias('uv_offset')] [ComponentModel.DefaultBindingProperty('uv_offset')] [Single[]] $UvOffset, -# Set the uv_scale of OBSTwoPassDropShadowShader +# Set the uv_scale of OBSFilterTemplateShader [Alias('uv_scale')] [ComponentModel.DefaultBindingProperty('uv_scale')] [Single[]] $UvScale, -# Set the uv_pixel_interval of OBSTwoPassDropShadowShader +# Set the uv_pixel_interval of OBSFilterTemplateShader [Alias('uv_pixel_interval')] [ComponentModel.DefaultBindingProperty('uv_pixel_interval')] [Single[]] $UvPixelInterval, -# Set the rand_f of OBSTwoPassDropShadowShader -[Alias('rand_f')] -[ComponentModel.DefaultBindingProperty('rand_f')] -[Single] -$RandF, -# Set the uv_size of OBSTwoPassDropShadowShader +# Set the uv_size of OBSFilterTemplateShader [Alias('uv_size')] [ComponentModel.DefaultBindingProperty('uv_size')] [Single[]] $UvSize, -# Set the shadow_offset_x of OBSTwoPassDropShadowShader -[Alias('shadow_offset_x')] -[ComponentModel.DefaultBindingProperty('shadow_offset_x')] -[Int32] -$ShadowOffsetX, -# Set the shadow_offset_y of OBSTwoPassDropShadowShader -[Alias('shadow_offset_y')] -[ComponentModel.DefaultBindingProperty('shadow_offset_y')] -[Int32] -$ShadowOffsetY, -# Set the shadow_blur_size of OBSTwoPassDropShadowShader -[Alias('shadow_blur_size')] -[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +# Set the rand_f of OBSFilterTemplateShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSFilterTemplateShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSFilterTemplateShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('loops')] [Int32] -$ShadowBlurSize, -# Set the shadow_color of OBSTwoPassDropShadowShader -[Alias('shadow_color')] -[ComponentModel.DefaultBindingProperty('shadow_color')] +$Loops, +# Set the local_time of OBSFilterTemplateShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the notes of OBSFilterTemplateShader +[ComponentModel.DefaultBindingProperty('notes')] [String] -$ShadowColor, -# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader -[Alias('is_alpha_premultiplied')] -[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] -[Management.Automation.SwitchParameter] -$IsAlphaPremultiplied, +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43663,11 +35329,37 @@ $UseShaderTime process { -$shaderName = 'two-pass-drop-shadow' -$ShaderNoun = 'OBSTwoPassDropShadowShader' +$shaderName = 'filter_template' +$ShaderNoun = 'OBSFilterTemplateShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +//My shader modified by Me for use with obs-shaderfilter month/year v.02 + +//Section to converting GLSL to HLSL - can delete if unneeded +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) + +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + uniform float4x4 ViewProj; uniform texture2d image; @@ -43675,115 +35367,36 @@ uniform float elapsed_time; uniform float2 uv_offset; uniform float2 uv_scale; uniform float2 uv_pixel_interval; -uniform float rand_f; uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ +uniform string notes< + string widget_type = "info"; +> = "add notes here"; -sampler_state textureSampler { - Filter = Linear; - AddressU = Border; - AddressV = Border; - BorderColor = 00000000; -}; - -struct VertData { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; -VertData mainTransform(VertData v_in) +float4 mainImage(VertData v_in) : TARGET { - VertData vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv * uv_scale + uv_offset; - return vert_out; + return image.Sample(textureSampler, v_in.uv); } -uniform int shadow_offset_x< - string label = "shadow offset x"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; ->; -uniform int shadow_offset_y< - string label = "shadow offset y"; - string widget_type = "slider"; - int minimum = -1000; - int maximum = 1000; - int step = 1; ->; -uniform int shadow_blur_size< - string label = "shadow blur size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; ->; - -uniform float4 shadow_color; - -uniform bool is_alpha_premultiplied; - -float4 mainImage(VertData v_in) : TARGET -{ - int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), - v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); - - float sampled_shadow_alpha = 0; - - for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) - { - float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} - -float4 mainImage_2_end(VertData v_in) : TARGET -{ - int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); - - float4 color = image.Sample(textureSampler, v_in.uv); - float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, - v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); - - float sampled_shadow_alpha = 0; - - for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) - { - float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); - sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; - } - - sampled_shadow_alpha /= shadow_blur_samples; - - float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); - - return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); -} +/* +**Shaders use the built in Draw technique** +**this section can be deleted** technique Draw { - pass p0 - { - vertex_shader = mainTransform(v_in); - pixel_shader = mainImage(v_in); - } - - pass p1 + pass { vertex_shader = mainTransform(v_in); - pixel_shader = mainImage_2_end(v_in); + pixel_shader = mainImage(v_in); } } +*/ ' } @@ -43882,37 +35495,143 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVCRShader { +function Get-OBSFire3Shader { -[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] +[Alias('Set-OBSFire3Shader','Add-OBSFire3Shader')] param( -# Set the vertical_shift of OBSVCRShader -[Alias('vertical_shift')] -[ComponentModel.DefaultBindingProperty('vertical_shift')] +# Set the ViewProj of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSFire3Shader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] [Single] -$VerticalShift, -# Set the distort of OBSVCRShader -[ComponentModel.DefaultBindingProperty('distort')] +$ElapsedTime, +# Set the uv_offset of OBSFire3Shader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSFire3Shader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSFire3Shader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSFire3Shader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSFire3Shader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] [Single] -$Distort, -# Set the vignet of OBSVCRShader -[ComponentModel.DefaultBindingProperty('vignet')] +$RandF, +# Set the rand_instance_f of OBSFire3Shader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] [Single] -$Vignet, -# Set the stripe of OBSVCRShader -[ComponentModel.DefaultBindingProperty('stripe')] +$RandInstanceF, +# Set the rand_activation_f of OBSFire3Shader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] [Single] -$Stripe, -# Set the vertical_factor of OBSVCRShader -[Alias('vertical_factor')] -[ComponentModel.DefaultBindingProperty('vertical_factor')] +$RandActivationF, +# Set the loops of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSFire3Shader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] [Single] -$VerticalFactor, -# Set the vertical_height of OBSVCRShader -[Alias('vertical_height')] -[ComponentModel.DefaultBindingProperty('vertical_height')] +$LocalTime, +# Set the Movement_Direction_Horizontal of OBSFire3Shader +[Alias('Movement_Direction_Horizontal')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Horizontal')] [Single] -$VerticalHeight, +$MovementDirectionHorizontal, +# Set the Movement_Direction_Vertical of OBSFire3Shader +[Alias('Movement_Direction_Vertical')] +[ComponentModel.DefaultBindingProperty('Movement_Direction_Vertical')] +[Single] +$MovementDirectionVertical, +# Set the Alpha_Percentage of OBSFire3Shader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Invert of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSFire3Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFire3Shader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFire3Shader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Color_To_Replace of OBSFire3Shader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Apply_To_Specific_Color of OBSFire3Shader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Full_Width of OBSFire3Shader +[Alias('Full_Width')] +[ComponentModel.DefaultBindingProperty('Full_Width')] +[Management.Automation.SwitchParameter] +$FullWidth, +# Set the Flame_Size of OBSFire3Shader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Single] +$FlameSize, +# Set the Spark_Grid_Height of OBSFire3Shader +[Alias('Spark_Grid_Height')] +[ComponentModel.DefaultBindingProperty('Spark_Grid_Height')] +[Single] +$SparkGridHeight, +# Set the Flame_Modifier of OBSFire3Shader +[Alias('Flame_Modifier')] +[ComponentModel.DefaultBindingProperty('Flame_Modifier')] +[Single] +$FlameModifier, +# Set the Flame_Tongue_Size of OBSFire3Shader +[Alias('Flame_Tongue_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Tongue_Size')] +[Single] +$FlameTongueSize, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -43943,405 +35662,421 @@ $UseShaderTime process { -$shaderName = 'VCR' -$ShaderNoun = 'OBSVCRShader' +$shaderName = 'fire-3' +$ShaderNoun = 'OBSFire3Shader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/ldjGzV -//Converted to OpenGL by Exeldro February 19, 2022 -uniform float vertical_shift< - string label = "vertical shift"; +//My effect modified by Me for use with obs-shaderfilter month/year v.02 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; + +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp + + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; + +uniform float Movement_Direction_Horizontal< + string label = "Movement Direction Horizontal"; string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 0.4; -uniform float distort< - string label = "distort"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; +uniform float Movement_Direction_Vertical< + string label = "Movement Direction Vertical"; string widget_type = "slider"; - float minimum = 0; - float maximum = 5.0; - float step = 0.001; -> = 1.2; -uniform float vignet< - string label = "vignet"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.01; +> = 0.0; + +#define iTime elapsed_time +#define iResolution float4(uv_size,uv_pixel_interval) +#define Movement_Direction float2(Movement_Direction_Horizontal, Movement_Direction_Vertical) + +uniform int Alpha_Percentage< + string label = "Alpha Percentage"; string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float stripe< - string label = "stripe"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_factor< - string label = "vertical factor"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 80; +uniform bool Invert = false; +uniform float lumaMin< + string label = "Luma Min"; string widget_type = "slider"; - float minimum = -5.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform float vertical_height< - string label = "vertical height"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1000.0; - float step = 0.1; -> = 30.0; + float maximum = 1.0; + float step = 0.01; +> = 0.04; +uniform bool Apply_To_Image = true; +uniform bool Replace_Image_Color = true; +uniform float4 Color_To_Replace; +uniform bool Apply_To_Specific_Color = false; -float onOff(float a, float b, float c) +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) { - return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + float2 uv = v_in.uv; + if(Invert) + uv = 1.0 - v_in.uv; + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; } -float ramp(float y, float start, float end) +int2 iMouse() { - float inside = step(start,y) - step(end,y); - float fact = (y-start)/(end-start)*inside; - return (1.-fact) * inside; - + return int2(Movement_Direction.x * uv_size.x, Movement_Direction.y * uv_size.y); } -float modu(float x, float y) + +float mod(float x, float y) { - return (x / y) - floor(x / y); + return x - y * floor(x / y); } -float stripes(float2 uv) +float2 mod2(float2 x, float2 y) { - return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; + return x - y * floor(x / y); } -float4 getVideo(float2 uv) +/*ps start*/ +#define PI 3.1415926535897932384626433832795 +uniform bool Full_Width = false; + +uniform float Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; + +uniform float Spark_Grid_Height< + string label = "Spark Grid Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; + +uniform float Flame_Modifier< + string label = "Flame Modifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; + +uniform float Flame_Tongue_Size< + string label = "Flame Tongue Size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 8.5; + +// +// Description : Array and textureless GLSL 2D/3D/4D simplex +// noise functions. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : ijm +// Lastmod : 20110822 (ijm) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. +// https://github.com/ashima/webgl-noise +// + +vec3 mod2893(vec3 x) { - float2 look = uv; - float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); - look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; - float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + - (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); - look.y = modu((look.y + vShift) , 1.); - return image.Sample(textureSampler, look); + return x - floor(x * (1.0 / 289.0)) * 289.0; } -float2 screenDistort(float2 uv) +vec4 mod289(vec4 x) { - uv -= float2(.5,.5); - uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); - uv += float2(.5,.5); - return uv; + return x - floor(x * (1.0 / 289.0)) * 289.0; } -float4 mainImage(VertData v_in) : TARGET +vec4 permute(vec4 x) { - float2 uv = v_in.uv; - uv = screenDistort(uv); - float4 video = getVideo(uv); - float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); - float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; - video += stripes(uv); - video *= vignette; - video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; - return float4(video.r, video.g, video.b ,1.0); + return mod289(((x * 34.0) + 1.0) * x); } -' -} -$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 -if (-not $myNoun) { - $myNoun = $myVerb - $myVerb = 'Get' +vec4 taylorInvSqrt(vec4 r) +{ + return 1.79284291400159 - 0.85373472095314 * r; } -switch -regex ($myVerb) { - Get { - $FilterNamePattern = "(?>$( - if ($FilterName) { - [Regex]::Escape($FilterName) - } - else { - [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' - } - ))" - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } else { - $obs.Inputs | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern - } - } - 'Remove' { - if ($SourceName) { - Get-OBSInput | - Where-Object InputName -eq $SourceName | - Get-OBSSourceFilterList | - Where-Object FilterName -Match $FilterNamePattern | - Remove-OBSSourceFilter - } - } - '(?>Add|Set)' { - $ShaderSettings = [Ordered]@{} - :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { - foreach ($parameterAttribute in $parameterMetadata.Attributes) { - if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } - $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] - if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { - $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] - } - continue nextParameter - } - } - if (-not $PSBoundParameters['FilterName']) { - $filterName = $PSBoundParameters['FilterName'] = $shaderName - } +float snoise(vec3 v) +{ + const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); - $ShaderFilterSplat = [Ordered]@{ - ShaderSetting = $ShaderSettings - FilterName = $FilterName - SourceName = $SourceName - } +// First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); - foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { - if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { - $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] - } - } +// Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); - if (-not $script:CachedShaderFilesFromCommand) { - $script:CachedShaderFilesFromCommand = @{} - } + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y - if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { - $MyObsPowerShellPath = Join-Path $home ".obs-powershell" - $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" - $shaderText | Set-Content -LiteralPath $ThisShaderPath - $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath - } - if ($script:CachedShaderFilesFromCommand[$shaderName]) { - $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName - } else { - $ShaderFilterSplat.ShaderText = $shaderText - } +// Permutations + i = mod2893(i); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); - if ($myVerb -eq 'Add') { - Add-OBSShaderFilter @ShaderFilterSplat - } else { - Set-OBSShaderFilter @ShaderFilterSplat - } - } -} +// Gradients: 7x7 points over a square, mapped onto an octahedron. +// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; -} + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) -} + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSVHSShader { + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); -[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] -param( -# Set the range of OBSVHSShader -[ComponentModel.DefaultBindingProperty('range')] -[Single] -$Range, -# Set the offsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('offsetIntensity')] -[Single] -$OffsetIntensity, -# Set the noiseQuality of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseQuality')] -[Single] -$NoiseQuality, -# Set the noiseIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('noiseIntensity')] -[Single] -$NoiseIntensity, -# Set the colorOffsetIntensity of OBSVHSShader -[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] -[Single] -$ColorOffsetIntensity, -# Set the Alpha_Percentage of OBSVHSShader -[Alias('Alpha_Percentage')] -[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] -[Single] -$AlphaPercentage, -# Set the Apply_To_Image of OBSVHSShader -[Alias('Apply_To_Image')] -[ComponentModel.DefaultBindingProperty('Apply_To_Image')] -[Management.Automation.SwitchParameter] -$ApplyToImage, -# Set the Replace_Image_Color of OBSVHSShader -[Alias('Replace_Image_Color')] -[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] -[Management.Automation.SwitchParameter] -$ReplaceImageColor, -# Set the Color_To_Replace of OBSVHSShader -[Alias('Color_To_Replace')] -[ComponentModel.DefaultBindingProperty('Color_To_Replace')] -[String] -$ColorToReplace, -# Set the Apply_To_Specific_Color of OBSVHSShader -[Alias('Apply_To_Specific_Color')] -[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] -[Management.Automation.SwitchParameter] -$ApplyToSpecificColor, -# The name of the source. This must be provided when adding an item for the first time -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('SceneItemName')] -[String] -$SourceName, -# The name of the filter. If this is not provided, this will default to the shader name. -[Parameter(ValueFromPipelineByPropertyName)] -[String] -$FilterName, -# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. -[Alias('ShaderContent')] -[String] -$ShaderText, -# If set, will force the recreation of a shader that already exists -[Management.Automation.SwitchParameter] -$Force, -# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) -[Management.Automation.SwitchParameter] -$PassThru, -# If set, will not wait for a response from OBS (this will be faster, but will not return anything) -[Management.Automation.SwitchParameter] -$NoResponse, -# If set, use the shader elapsed time, instead of the OBS system elapsed time -[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] -[Management.Automation.SwitchParameter] -$UseShaderTime -) + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0, 0.0, 0.0, 0.0)); + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; -process { -$shaderName = 'VHS' -$ShaderNoun = 'OBSVHSShader' -if (-not $psBoundParameters['ShaderText']) { - $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 -//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 -//Converted to OpenGL by Exeldro February 19, 2022 -//Use improved input fields by Exeldro April 15, 2023 -uniform float range< - string label = "Wave size (0.05)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 0.20; - float step = 0.01; -> = 0.05; -uniform float offsetIntensity< - string label = "Offset intensity (0.02)"; - string widget_type = "slider"; - float minimum = 0.01; - float maximum = 0.20; - float step = 0.01; -> = 0.02; -uniform float noiseQuality< - string label = "Noise number of lines (250)"; - string widget_type = "slider"; - float minimum = 1.0; - float maximum = 1000.0; - float step = 10.0; -> = 250.0; -uniform float noiseIntensity< - string label = "Noise intensity (0.88)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.01; -> = 0.88; -uniform float colorOffsetIntensity< - string label = "Color offset intensity (1.3)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 10.0; - float step = 0.1; -> = 1.3; -uniform float Alpha_Percentage< - string label = "Aplha percentage (100.0)"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 100.0; - float step = 1.0; -> = 100.0; -uniform bool Apply_To_Image; -uniform bool Replace_Image_Color; -uniform float4 Color_To_Replace; -uniform bool Apply_To_Specific_Color; + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); -float dot2(float2 a,float2 b){ - return a.x*b.x+a.y*b.y; -} +//Normalise gradients + //vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + vec4 norm = rsqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; -float rand(float2 co) -{ - return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); +// Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); } -float verticalBar(float pos, float uvY, float offset) -{ - float edge0 = (pos - range); - float edge1 = (pos + range); +////////////////////////////////////////////////////////////// - float x = smoothstep(edge0, pos, uvY) * offset; - x -= smoothstep(pos, edge1, uvY) * offset; - return x; +// PRNG +// From https://www.shadertoy.com/view/4djSRW +float prng(in vec2 seed) +{ + seed = fract(seed * vec2(5.3983, 5.4427)); + seed += dot(seed.yx, seed.xy + vec2(21.5351, 14.3137)); + return fract(seed.x * seed.y * 95.4337); } -float modu(float x, float y) +////////////////////////////////////////////////////////////// + +float noiseStack(vec3 pos, int octaves, float falloff) { - return (x / y) - floor(x / y); + float noise = snoise(vec3(pos)); + float off = 1.0; + if (octaves > 1) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 2) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + if (octaves > 3) + { + pos *= 2.0; + off *= falloff; + noise = (1.0 - off) * noise + off * snoise(vec3(pos)); + } + return (1.0 + noise) / 2.0; } -float dot4(float4 a,float4 b){ - return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +vec2 noiseStackUV(vec3 pos, int octaves, float falloff, float diff) +{ + float displaceA = noiseStack(pos, octaves, falloff); + float displaceB = noiseStack(pos + vec3(3984.293, 423.21, 5235.19), octaves, falloff); + return vec2(displaceA, displaceB); } float4 mainImage(VertData v_in) : TARGET { - float2 uv = v_in.uv; - for (float i = 0.0; i < 0.71; i += 0.1313) + float2 UV = (1.0 - v_in.uv) * uv_scale; + if (Invert) + UV = v_in.uv * uv_scale; + float alpha = saturate(Alpha_Percentage * .01); + float flame_size = clamp(Flame_Size * .01, 0.0, 4.0); + + vec2 resolution = (.25 * uv_scale * UV.xy) + (0.75 * uv_scale); + if (Full_Width) { - float d = modu(elapsed_time * i, 1.7); - float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); - o *= offsetIntensity; - uv.x += verticalBar(d, uv.y, o); + resolution = (2.0 * (UV.xy)) / 1.0; //iResolution.xy; + } - float uvY = uv.y; - uvY *= noiseQuality; - uvY = float(int(uvY)) * (1.0 / noiseQuality); - float noise = rand(float2(elapsed_time * 0.00001, uvY)); - uv.x += noise * noiseIntensity / 100.0; - - float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; - float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; - - float4 rgba = image.Sample(textureSampler, uv); - float r = image.Sample(textureSampler, uv + offsetR).r; - float g = image.Sample(textureSampler, uv + offsetG).g; - float b = rgba.b; - - rgba = float4(r, g, b, rgba.a); + resolution.x = mul(resolution.x, 1 / 1); + float time = iTime * (Speed * 0.01); + //vec2 drag = iMouse().xy; + vec2 offset = iMouse().xy; + // + float xpart = UV.x / resolution.x; + float ypart = UV.y / resolution.y; + // + + float ypartClip = UV.y / ( flame_size * 75.0); + float ypartClippedFalloff = clamp(2.0 - ypartClip, 0.0, 1.0); + float ypartClipped = min(ypartClip, 1.0); + float ypartClippedn = (1 - ypartClipped); + // + float xfuel = pow(1.0 - abs(2.0 * xpart - 1.0), 0.5); //pow(1.0-abs(2.0*xpart-1.0),0.5); + // + float timeSpeed = 0.5 * (Speed * 0.01); + float realTime = -1.0 * timeSpeed * time; + // + vec2 coordScaled = -1 * Flame_Tongue_Size * UV - 0.1 * offset; + vec3 position = vec3(coordScaled, 0.0); // +vec3(1223.0, 6434.0, 8425.0); + vec3 flow = vec3(4.1 * (0.5 - xpart) * pow(ypartClippedn, 4.0), -2.0 * xfuel * pow(ypartClippedn, 64.0), 0.0); + vec3 timing = realTime * vec3(0.0, -1.7, 1.1) + flow; + // + vec3 displacePos = vec3(1.0, 0.5, 1.0) * 2.4 * position + realTime * vec3(0.01, -0.7, 1.3); + vec3 displace3 = vec3(noiseStackUV(displacePos, 2, 0.4, 0.1), 0.0); + // + vec3 noiseCoord = (vec3(2.0, 1.0, 1.0) * position + timing + 0.4 * displace3) / 1.0; + float noise = noiseStack(noiseCoord, 3, 0.4); + // + float flames = pow(ypartClipped, 0.3 * xfuel) * pow(noise, 0.3 * xfuel); + // + float f = ypartClippedFalloff * pow(Flame_Modifier - flames * flames * flames, 8.0); + float fff = f * f * f; + vec3 fire = 1.5 * vec3(f, fff, fff * fff); + // + // smoke + float smokeNoise = 0.5 + snoise(0.4 * position + timing * vec3(1.0, 1.0, 0.2)) / 2.0; + float smokePart = 0.3 * pow(xfuel, 3.0) * pow(ypart, 2.0) * (smokeNoise + 0.4 * (1.0 - noise)); + vec3 smoke = vec3(smokePart, smokePart, smokePart); + // + // sparks + float sparkGridSize = Spark_Grid_Height; + vec2 sparkCoord = UV *uv_size - vec2(2.0 * offset.x, 190.0 * sin(realTime)); + sparkCoord -= 30.0 * noiseStackUV(0.01 * vec3(sparkCoord, 15.0 * time), 1, 0.4, 0.1); + sparkCoord += 100.0 * flow.xy; + if (mod(sparkCoord.y / sparkGridSize, 2.0) < 1.0) + sparkCoord.x += 0.5 * sparkGridSize; + vec2 sparkGridIndex = vec2(floor(sparkCoord / sparkGridSize)); + float sparkRandom = prng( sparkGridIndex); + float sparkLife = min(10.0 * (1.0 - min((sparkGridIndex.y + (190.0 * realTime / sparkGridSize)) / (24.0 - 20.0 * sparkRandom), 1.0)), 1.0); + vec3 sparks = vec3(0.0, 0.0, 0.0); + if (sparkLife > 0.0) + { + float sparkSize = xfuel * xfuel * sparkRandom * 0.08; + float sparkRadians = 999.0 * sparkRandom * 2.0 * PI + 2.0 * time; + vec2 sparkCircular = vec2(sin(sparkRadians), cos(sparkRadians)); + vec2 sparkOffset = (0.5 - sparkSize) * sparkGridSize * sparkCircular; + vec2 sparkModulus = mod2(sparkCoord + sparkOffset, float2(sparkGridSize, sparkGridSize)) - 0.5 * float2(sparkGridSize, sparkGridSize); + float sparkLength = length(sparkModulus); + float sparksGray = max(0.0, 1.0 - sparkLength / (sparkSize * sparkGridSize)); + sparks = sparkLife * sparksGray * vec3(1.0, 0.3, 0.0); + } + // + float4 rgba = vec4(max(fire, sparks) + smoke, 1.0); + // remove dark areas per user + float luma_fire = dot(rgba.rgb, float3(0.299, 0.587, 0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire, 0.0, alpha); + float4 color; float4 original_color; - if (Apply_To_Image) + if (Apply_To_Image) { - color = image.Sample(textureSampler, v_in.uv); - original_color = color; - float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); - if (Replace_Image_Color) - color = float4(luma,luma,luma,luma); - rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); + } + else + { + rgba = color; + } } if (Apply_To_Specific_Color) @@ -44349,12 +36084,21 @@ float4 mainImage(VertData v_in) : TARGET color = image.Sample(textureSampler, v_in.uv); original_color = color; color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; - rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + rgba = lerp(original_color, color, alpha); } - + return rgba; } +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -44452,24 +36196,63 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVignettingShader { +function Get-OBSFireShader { -[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] +[Alias('Set-OBSFireShader','Add-OBSFireShader')] param( -# Set the innerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('innerRadius')] -[Single] -$InnerRadius, -# Set the outerRadius of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('outerRadius')] +# Set the Alpha_Percentage of OBSFireShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Int32] +$AlphaPercentage, +# Set the Speed of OBSFireShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Int32] +$Speed, +# Set the Flame_Size of OBSFireShader +[Alias('Flame_Size')] +[ComponentModel.DefaultBindingProperty('Flame_Size')] +[Int32] +$FlameSize, +# Set the Fire_Type of OBSFireShader +[Alias('Fire_Type')] +[ComponentModel.DefaultBindingProperty('Fire_Type')] +[Int32] +$FireType, +# Set the Invert of OBSFireShader +[ComponentModel.DefaultBindingProperty('Invert')] +[Management.Automation.SwitchParameter] +$Invert, +# Set the lumaMin of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMin')] [Single] -$OuterRadius, -# Set the opacity of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('opacity')] +$LumaMin, +# Set the lumaMinSmooth of OBSFireShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] [Single] -$Opacity, -# Set the notes of OBSVignettingShader -[ComponentModel.DefaultBindingProperty('notes')] +$LumaMinSmooth, +# Set the Apply_To_Image of OBSFireShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSFireShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSFireShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSFireShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSFireShader +[ComponentModel.DefaultBindingProperty('Notes')] [String] $Notes, # The name of the source. This must be provided when adding an item for the first time @@ -44502,62 +36285,256 @@ $UseShaderTime process { -$shaderName = 'vignetting' -$ShaderNoun = 'OBSVignettingShader' +$shaderName = 'fire' +$ShaderNoun = 'OBSFireShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 -uniform float innerRadius< - string label = "inner radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.9; -uniform float outerRadius< - string label = "outer radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.5; -uniform float opacity< - string label = "opacity"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.8; -uniform string notes< - string widget_type = "info"; -> = "inner radius will always be shown, outer radius is the falloff"; - -float4 mainImage(VertData v_in) : TARGET +//fire shader modified by Charles Fettinger for use with obs-shaderfilter 07/20 v.6 +// https://github.com/Oncorporation/obs-shaderfilter plugin +// https://www.shadertoy.com/view/MtcGD7 original version +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +//v.5 +// flicker +// flame type +// apply to image +// replace image color +// speed +// flame size +// alpha +// invert direction/position + + +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp + +uniform int Alpha_Percentage< + string label = "Aplha Percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int Speed< + string label = "Speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 100; +uniform int Flame_Size< + string label = "Flame Size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 70; +uniform int Fire_Type< + string label = "Fire Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Smaller and more whisps"; + int option_1_value = 1; + string option_1_label = "Larger and more volume"; +> = 1; + +uniform bool Invert < + string name = "Invert"; +> = false; +uniform float lumaMin< + string label = "Luma min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma min smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.04; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Luma cuts reveals background, flame size is percentage screen size, Alpha Percentage adjusts color"; + +vec3 rgb2hsv(vec3 c) { - float PI = 3.1415926535897932384626433832795;//acos(-1); + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - float4 c0 = image.Sample(textureSampler, v_in.uv); - float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; - - float xTrans = (v_in.uv.x * 2) - 1; - float yTrans = 1 - (v_in.uv.y * 2); - - float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} - float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); - float factor = 1 - subtraction; +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} - float4 vignetColor = c0 * factor; - vignetColor *= verticalDim; +float rand(vec2 n) +{ + return fract(sin(cos(dot(n, vec2(12.9898, 12.1414)))) * 83758.5453); + //return sin(rand_f, n); +} - vignetColor *= opacity; - c0 *= 1-opacity; +float noise(vec2 n) +{ + const vec2 d = vec2(0.0, 1.0); + vec2 b = floor(n), f = smoothstep(vec2(0.0, 0.0), vec2(1.0, 1.0), fract(n)); + return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); +} - float4 output_color = c0 + vignetColor; +float fbm(vec2 n) +{ + float total = 0.0, amplitude = 1.0; + for (int i = 0; i < 5; i++) + { + total += noise(n) * amplitude; + n += n * 1.7; + amplitude *= 0.47; + } + return total; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale; + float flame_size = clamp(Flame_Size * .01,-5,5); + + // inverting direction is logically inverted to allow the bottom up to be normal + float fire_base = (v_in.uv.y / iResolution.y); + float2 fire_pix = v_in.uv.xy + float2(flame_size -1,0); + float direction = -1.0 * clamp(Speed*.01,-5,5); + if (!Invert) + { + direction *= -1.0; + fire_base = 1 - fire_base; + fire_pix = 1 - fire_pix; + } + float iTime = direction * elapsed_time; + + const vec3 c1 = vec3(0.5, 0.0, 0.1); + const vec3 c2 = vec3(0.9, 0.1, 0.0); + const vec3 c3 = vec3(0.2, 0.1, 0.7); + const vec3 c4 = vec3(1.0, 0.9, 0.1); + const vec3 c5 = vec3(0.1, 0.1, 0.1); + const vec3 c6 = vec3(0.9, 0.9, 0.9); + + vec2 speed = vec2(1.2, 0.1) * clamp(Speed*.01,-5,5); + float shift = 1.327 * (1/flame_size) - sin(iTime * 2.0) / 2.4; + float alpha = saturate(Alpha_Percentage * .01); + + //change the constant term for all kinds of cool distance versions, + //make plus/minus to switch between + //ground fire and fire rain! + float dist = 3.5 - sin(iTime * 0.4) / 1.89; + + vec2 p = fire_pix * dist / iResolution.xx; + p.x -= iTime / 1.1; + float3 black = float3(0,0,0); + vec3 fire; + + if (Fire_Type == 1) + { + //fire version 1 larger and more volume + float q = fbm(p - iTime * 0.01 + 1.0 * sin(iTime) / 10.0); + float qb = fbm(p - iTime * 0.002 + 0.1 * cos(iTime) / 5.0); + float q2 = fbm(p - iTime * 0.44 - 5.0 * cos(iTime) / 7.0) -6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0 * cos(iTime) / 30.0) -4.0; + float q4 = fbm(p - iTime * 2.0 - 20.0 * sin(iTime) / 20.0) +2.0; + q = (q + qb - .4 * q2 - 2.0 * q3 + .6 * q4) / 3.8; + + vec2 r = vec2(fbm(p + q / 2.0 - iTime* speed.x - p.x - p.y), + fbm(p - q - iTime* speed.y)) ; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + fire = vec3(c * max(cos(shift * fire_base) - (rand_f *.05),0.05)); + + fire += .05; + fire.r *= .8; + vec3 hsv = rgb2hsv(fire); + hsv.y *= hsv.z * 1.1; + hsv.z *= hsv.y * 1.13; + hsv.y = (2.2 - hsv.z * .9) * 1.20; + fire = hsv2rgb(hsv); + } + else + { + // fire version 0 - smaller and more whisps + p += (rand_f *.01); + float q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0); + float qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0); + float q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0; + float q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0)-4.0; + float q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0; + q = (q + qb - .4 * q2 -2.0*q3 + .6*q4)/3.8; + + vec2 r = vec2(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), + fbm(p + q - iTime * speed.y)) * shift; + vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y); + //fire = vec3(1.0/(pow(c+1.61,vec3(4.0,4.0,4.0))) * max(cos(shift * fire_base),0)); + + fire = vec3(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0)) ;//* max(.1,(cos(shift * fire_base))); + fire += (black*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), v_in.uv.y); + fire = fire/(1.0+max(black,fire)); + } + float4 rgba = vec4(fire.x, fire.y, fire.z, alpha); - return float4(output_color); + // remove dark areas per user + float luma_fire = dot(rgba.rgb,float3(0.299,0.587,0.114)); + float luma_min_fire = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma_fire); + rgba.a = clamp(luma_min_fire,0.0,alpha); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + if (color.a > 0.0) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, lerp(original_color,rgba * color,rgba.a), alpha); + } + else + { + rgba = color; + } + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + return rgba; } + + + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -44655,18 +36632,14 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVoronoiPixelationShader { +function Get-OBSFireworks2Shader { -[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] +[Alias('Set-OBSFireworks2Shader','Add-OBSFireworks2Shader')] param( -# Set the pixH of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('pixH')] +# Set the Speed of OBSFireworks2Shader +[ComponentModel.DefaultBindingProperty('Speed')] [Single] -$PixH, -# Set the alternative of OBSVoronoiPixelationShader -[ComponentModel.DefaultBindingProperty('alternative')] -[Management.Automation.SwitchParameter] -$Alternative, +$Speed, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -44697,98 +36670,128 @@ $UseShaderTime process { -$shaderName = 'voronoi-pixelation' -$ShaderNoun = 'OBSVoronoiPixelationShader' +$shaderName = 'fireworks2' +$ShaderNoun = 'OBSFireworks2Shader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro +// based on https://www.shadertoy.com/view/4dBGRw -uniform float pixH< - string label = "Size"; +uniform float Speed< + string label = "Speed"; string widget_type = "slider"; - float minimum = 4.0; - float maximum = 500.0; - float step = 0.01; + float minimum = 0.0; + float maximum = 200.0; + float step = 1.0; > = 100.0; -uniform bool alternative; -float2 fract2(float2 v){ - return float2(v.x - floor(v.x), v.y - floor(v.y)); +#ifndef OPENGL +#define mat2 float2x2 +#define mix lerp +float mod(float x, float y) +{ + return x - y * floor(x / y); } +#endif +//Creates a diagonal red-and-white striped pattern. +float3 barberpole(float2 pos, float2 rocketpos){ + float d = (pos.x-rocketpos.x)+(pos.y-rocketpos.y); + float3 col=float3(1.0,1.0,1.0); -float2 random2( float2 p ) { - return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); + d = mod(d*20.,2.0); + if(d>1.0){ + col=float3(1.0,0.0,0.0); + } + return col; } -float2 randomSpin(float2 p, float f){ - return 1.0 * float2( - cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), - sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); + +float4 rocket(float2 pos, float2 rocketpos){ + float4 col = float4(0.0,0.0,0.0,0.0); + float f = 0.; + float absx= abs(rocketpos.x - pos.x); + float absy = abs(rocketpos.y-pos.y); + //wooden stick + if(absx<0.01&&absy<0.22){ + col=float4(1.0,0.5,0.5,1.0); + } + + //Barberpole + + if(absx<0.05&&absy<0.15){ + col=float4(barberpole(pos, rocketpos),1.0); + } + //Rocket Point + float pointw=(rocketpos.y-pos.y-0.25)*-0.7; + if((rocketpos.y-pos.y)>0.1){ + f=smoothstep(pointw-0.001,pointw+0.001,absx); + + col=mix(float4(1.0,0.0,0.0,1.0),col, f); + } + //Shadow + + f =-.5 + smoothstep(-0.05, 0.05, (rocketpos.x-pos.x)); + col.rgb *= 0.7+f; + + return col; } -float4 VoronoiPixelation(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); - float2 point1 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point1 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; - } - } - } - float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x ; - - return image.Sample(textureSampler, nPoint); +float rand(float val, float seed){ + return cos(val*sin(val*seed)*seed); } -float4 VoronoiPixelation2(float2 uv, float pixH ){ - float2 pixInt = fract2(uv * pixH); - float2 pixExt = floor(uv * pixH); - float m_dist = 10.0; - float2 relClos = float2(0.0, 0.0); - float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); +float distance2( in float2 a, in float2 b ) { return dot(a-b,a-b); } - for (int y= -3; y <= 3; y++) { - for (int x= -3; x <= 3; x++) { - float2 neighbor = float2(float(x),float(y)); - float2 point2 = random2(pixExt + neighbor); - float2 relRot = randomSpin(pixExt + neighbor, 0.5); - float2 diff = neighbor + relRot + point2 - pixInt; - float dist = length(diff); - if(dist < m_dist){ - m_dist = dist; - relClos = neighbor; - } - } + +float4 drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength){ + float4 col= float4(0.0,0.0,0.0,0.0); + float2 pp = float2(1.0,0.0); + mat2 rr = mat2( cos(1.0), -sin(1.0), sin(1.0), cos(1.0) ); + for(float i=1.0;i<=128.0;i++){ + float d=rand(i, seed); + float fade=(i/128.0)*time; + float2 particpos = cpos + time*pp*d; + pp = mul(rr,pp); + col.rgb = mix(particolor/fade, col, smoothstep(0.0, 0.0001, distance2(particpos, pos))); } - float2 nPoint = pixExt + relClos + random2(pixExt + relClos); - nPoint = nPoint / pixH; - nPoint.x = nPoint.x * uv_scale.x; - - return image.Sample(textureSampler, nPoint); + col.rgb*=smoothstep(0.0,1.0,(timelength-time)/timelength); + col.a = col.r+col.g+col.b; + return col; +} +float4 drawFireworks(float time, float2 uv, float3 particolor, float seed){ + + float timeoffset = 2.0; + float4 col=float4(0.0,0.0,0.0,0.0); + if(time<=0.){ + return col; + } + time *= Speed /100.0; + if(mod(time, 6.0)>timeoffset){ + col= drawParticles(uv, particolor, mod(time, 6.0)-timeoffset, float2(rand(ceil(time/6.0),seed),-0.5), 0.5, ceil(time/6.0), seed); + }else{ + + col= rocket(uv*3., float2(3.*rand(ceil(time/6.0),seed),3.*(-0.5+(timeoffset-mod(time, 6.0))))); + } + return col; } - float4 mainImage(VertData v_in) : TARGET { - if (alternative) { - return VoronoiPixelation2(v_in.uv, pixH); - } else { - return VoronoiPixelation(v_in.uv, pixH); - } + float2 uv =float2(1.0,1.0) - 2.0* v_in.uv; + uv.y = -uv.y; + uv.x *= uv_size.x/uv_size.y; + float4 col = image.Sample(textureSampler, v_in.uv); + //col.rgb += 0.1*uv.y; + float4 c; + c = drawFireworks(elapsed_time , uv,float3(1.0,0.1,0.1), 1.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-2.0, uv,float3(0.0,1.0,0.5), 2.); + col = mix(col, c, c.a); + c = drawFireworks(elapsed_time-4.0, uv,float3(1.0,1.0,0.1), 3.); + col = mix(col, c, c.a); + + return col; } ' } @@ -44887,59 +36890,25 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSWalkingDeadPixelFixerShader { +function Get-OBSFireworksShader { -[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] +[Alias('Set-OBSFireworksShader','Add-OBSFireworksShader')] param( -# Set the Scan_Width of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Width')] -[ComponentModel.DefaultBindingProperty('Scan_Width')] -[Int32] -$ScanWidth, -# Set the Scan_Height of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Height')] -[ComponentModel.DefaultBindingProperty('Scan_Height')] -[Int32] -$ScanHeight, -# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Offset_X')] -[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] -[Int32] -$ScanOffsetX, -# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader -[Alias('Scan_Offset_Y')] -[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] -[Int32] -$ScanOffsetY, -# Set the Show_Border of OBSWalkingDeadPixelFixerShader -[Alias('Show_Border')] -[ComponentModel.DefaultBindingProperty('Show_Border')] +# Set the show_flash of OBSFireworksShader +[Alias('show_flash')] +[ComponentModel.DefaultBindingProperty('show_flash')] [Management.Automation.SwitchParameter] -$ShowBorder, -# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader -[Alias('Contrast_Threshold')] -[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] -[Single] -$ContrastThreshold, -# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader -[Alias('Min_Cluster_Size')] -[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] -[Int32] -$MinClusterSize, -# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader -[Alias('Max_Cluster_Size')] -[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] -[Int32] -$MaxClusterSize, -# Set the Show_Green of OBSWalkingDeadPixelFixerShader -[Alias('Show_Green')] -[ComponentModel.DefaultBindingProperty('Show_Green')] +$ShowFlash, +# Set the show_stars of OBSFireworksShader +[Alias('show_stars')] +[ComponentModel.DefaultBindingProperty('show_stars')] [Management.Automation.SwitchParameter] -$ShowGreen, -# Set the Bypass of OBSWalkingDeadPixelFixerShader -[ComponentModel.DefaultBindingProperty('Bypass')] +$ShowStars, +# Set the use_transparancy of OBSFireworksShader +[Alias('use_transparancy')] +[ComponentModel.DefaultBindingProperty('use_transparancy')] [Management.Automation.SwitchParameter] -$Bypass, +$UseTransparancy, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -44970,207 +36939,175 @@ $UseShaderTime process { -$shaderName = 'walking-dead-pixel-fixer' -$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' +$shaderName = 'fireworks' +$ShaderNoun = 'OBSFireworksShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter -// by Eegee http://github.com/eegee/ -// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter -// Copyright ©️ 2022 by SkeletonBow -// License: GNU General Public License, version 2 -// Contact info: -// Twitter: -// Twitch: -// -// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. -// The pixels located in the user configured scan area and passing the threshold settings will have its colors -// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. -// -// Changelog: -// 0.01 - Initial release -// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. - -uniform int Scan_Width< - string label = "Scan area width"; - int minimum = 1; - int maximum = 2560; - int step = 1; -> = 75; - -uniform int Scan_Height< - string label = "Scan area height"; - int minimum = 1; - int maximum = 1440; - int step = 1; -> = 120; - -uniform int Scan_Offset_X< - string label = "Scan area offset X"; - int minimum = 0; - int maximum = 2560; - int step = 1; -> = 110; - -uniform int Scan_Offset_Y< - string label = "Scan area offset Y"; - int minimum = 0; - int maximum = 1440; - int step = 1; -> = 20; - -uniform bool Show_Border< - string label = "Show scan area border in red"; - string widget_type = "checkbox"; -> = true; +#ifndef OPENGL +#define mat2 float2x2 +#define fract frac +#define mix lerp +#endif -uniform float Contrast_Threshold< - string label = "Contrast threshold"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 1.0; - float step = 0.01; -> = 0.05; +uniform bool show_flash = true; +uniform bool show_stars = true; +uniform bool use_transparancy = true; -uniform int Min_Cluster_Size< - string label = "Min cluster size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 600; - int step = 2; -> = 324; +float distLine(float2 p, float2 a, float2 b) { + float2 pa = p - a; + float2 ba = b - a; + float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * t); +} -uniform int Max_Cluster_Size< - string label = "Max cluster size"; - string widget_type = "slider"; - int minimum = 0; - int maximum = 600; - int step = 2; -> = 400; +float linef(float2 uv, float2 a, float2 b, float w) { + //return smoothstep(w, w - 0.01, distLine(uv, a, b)); + return w / distLine(uv, a, b); +} -uniform bool Show_Green< - string label = "Show matches in green"; - string widget_type = "checkbox"; -> = true; +float N21(float2 p) { + p = fract(p * float2(233.34, 851.73)); + p += dot(p, p + 23.45); + return fract(p.x * p.y); +} -uniform bool Bypass< - string label = "Bypass"; - string widget_type = "checkbox"; -> = false; +float2 N22(float2 p) { + float n = N21(p); + return float2(n, N21(p + n)); +} +float N11(float n) { + return fract(sin(dot(float2(cos(n), sin(n)) ,float2(27.9898, 38.233))) * 88.5453); +} -#define SAMPLE_RADIUS 9 +float particle(float2 uv, float2 p, float2 v, float r, float t) { + float g = -9.81; + float x = p.x + v.x * t; + float y = p.y + v.y * t + g / 2.0 * t * t; + float2 j = (float2(x, y) - uv) * 20.0; + float sparkle = 1.0 / dot(j, j); + return sparkle; +} -float luminance(float3 color) -{ - return dot(color, float3(0.299, 0.587, 0.114)); +float2 p1(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(t, 5.0), 0.0, h)); } -void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) -{ - float3 sumColor = float3(0.0, 0.0, 0.0); - float weightSum = 0.0; - contrastCount = 0; +float2 p2(float2 p, float h, float t) { + return float2(p.x, p.y + clamp(pow(0.95 * t, 5.0), 0.0, h)); +} - float3 centerColor = image.Sample(textureSampler, uv).rgb; - float centerLum = luminance(centerColor); +float endTime(float h) { + return pow(h, 1.0 / 5.0) * 1.1; +} - for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) - { - for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) - { - if (x == 0 && y == 0) continue; +float explosion(float2 uv, float2 p, float s, float n, float f, float t) { - float2 offset = float2(x, y) / uv_size; - float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); + float m = 0.0; + float dt = 0.5; + float seed2 = 0.32; + for(float i = 0.0; i < n; i++) { + seed2 += i; + float2 rand = float2(1.0, 2.0) * (float2(-1.0, 1.0) + 2.0 * N22(float2(seed2, i))); + float2 v = float2(cos(seed2), sin(seed2)) + rand; + m += particle(uv, p, v, s, t) * smoothstep(2.0, 2.0 - dt, t) * smoothstep(0.0, dt, t); + } + return m; +} - // skip central pixel - if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && - ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) - continue; +float fireworks(float2 uv, float2 p, float h, float n, float s, float f, float t) { + float2 p1v = p1(p, h, t); + float e = endTime(h); + return explosion(uv, p1v, s, n, f, t - e * 0.9); +} - float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; - float lum = luminance(sampleColor); +float shaft(float2 uv, float2 p, float w, float h, float t) { + float2 p1v = p1(p, h, t) + float2(0.0, 0.3); + float2 p2v = p2(p, h, t); + float e = 1.0 / 0.95 * endTime(h); + float2 j = (p1v - uv) * 15.0; + float sparkle = 1.0 / dot(j, j); + return (linef(uv, p1v, p2v, w) + sparkle) * smoothstep(e, e - 0.5, t) * 0.5; +} - float weight = 1.0; - sumColor += sampleColor * weight; - weightSum += weight; +float3 base(float2 uv) { + return 0.5 + 0.5 * cos(elapsed_time + uv.xyx + float3(0, 2, 4)); +} - if (abs(lum - centerLum) >= contrastThreshold) - contrastCount++; - } - } +float back(float2 uv, float2 p, float t) { + float dt = 0.3; + float j = length(p - uv); + float m = exp(-0.005 * j * j); + return 0.2 * m * smoothstep(-dt / 4.0, 0.0, t) * smoothstep(dt, 0.0, t); +} - if (weightSum > 0) - { - avgColor = sumColor / weightSum; - } - else - { - avgColor = centerColor; - } - avgLuminance = luminance(avgColor); +float stars(float2 uv) { + float r = N21(uv); + return smoothstep(0.001, 0.0, r); } -float4 mainImage(VertData v_in) : TARGET +float mod(float x, float y) { - float2 uv = v_in.uv; - float2 pos = v_in.pos.xy; - - float4 tex = image.Sample(textureSampler, uv); - float3 color = tex.rgb; - - if (!Bypass) - { - int pixX = (int)round(pos.x); - int pixY = (int)round(pos.y); - - int borderwidth = 2; - - bool insideScan = - (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && - (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); - - bool borderingScan = - (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && - (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); - - if (insideScan) - { - float3 avgColor; - float avgLum; - int contrastCount; - - sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); - - if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) - { - color = avgColor; - - if (Show_Green) - { - int left = pixX - borderwidth; - int right = pixX + borderwidth; - int top = pixY - borderwidth; - int bottom = pixY + borderwidth; - - bool onOutline = - abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || - abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; + return x - y * floor(x / y); +} - if (onOutline) - return float4(0.0, 1.0, 0.0, 1.0); - } - } +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv - float2(0.5,0.5); + uv.y = uv.y * -1; + float t = elapsed_time / 10.0; + float scale = 10.0; + uv *= scale; + // + float4 col = image.Sample(textureSampler, v_in.uv); + if(show_stars){ + float c = stars(uv); + if(use_transparancy){ + col += float4(c,c,c,c)*(1.0-col.a); + }else{ + col += float4(c,c,c,c);//*(1.0-orig_col.a); } - else if (Show_Border && borderingScan) - { - return float4(1.0, 0.0, 0.0, 0.5); + + } + + float a = -0.035 * sin(t * 15.0); + float co = cos(a); + float si = sin(a); + mat2 trans1 = mat2(float2(co, si), float2(-si, co)); + float2 trans2 = float2(-15.0 * a, 0.0); +#ifndef OPENGL + uv = mul(uv, trans1); +#else + uv *= trans1; +#endif + uv += trans2; + + for(float i = 0.0; i < 1.0; i += 1.0 / 8.0) { + float ti = mod(t * 9.0 - i * 5.0, 4.0); + float scale = mix(2.0, 0.3, ti / 4.0); + float2 uvs = uv * scale; + float rand = N11(i); + float h = 10.0 + rand * 4.0; + float w = 0.02; + float n = 80.0; + float s = 0.9; + float f = 1.5; + float2 p = float2(mix(-8.0, 8.0, rand), -10.0); + float fw = fireworks(uvs, p, h, n, s, f, ti); + float3 bc = base(uv); + col += float4(bc*fw, fw); + col += shaft(uvs, p, w, h, ti); + if(show_flash){ + if(use_transparancy){ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8)*col.a; + }else{ + col += back(uvs, float2(p.x, p.y + h), ti - 1.8); + } } } - return float4(color, tex.a); + + return col; } - ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -45268,48 +37205,24 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZigZagShader { +function Get-OBSFisheyeShader { -[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] +[Alias('Set-OBSFisheyeShader','Add-OBSFisheyeShader')] param( -# Set the radius of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('radius')] -[Single] -$Radius, -# Set the angle of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('angle')] -[Single] -$Angle, -# Set the period of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('period')] -[Single] -$Period, -# Set the amplitude of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('amplitude')] -[Single] -$Amplitude, -# Set the center_x of OBSZigZagShader -[Alias('center_x')] -[ComponentModel.DefaultBindingProperty('center_x')] +# Set the center_x_percent of OBSFisheyeShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] [Single] -$CenterX, -# Set the center_y of OBSZigZagShader -[Alias('center_y')] -[ComponentModel.DefaultBindingProperty('center_y')] +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] [Single] -$CenterY, -# Set the phase of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('phase')] +$CenterYPercent, +# Set the power of OBSFisheyeShader +[ComponentModel.DefaultBindingProperty('power')] [Single] -$Phase, -# Set the animate of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('animate')] -[Int32] -$Animate, -# Set the notes of OBSZigZagShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$Power, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -45340,122 +37253,51 @@ $UseShaderTime process { -$shaderName = 'ZigZag' -$ShaderNoun = 'OBSZigZagShader' +$shaderName = 'fisheye' +$ShaderNoun = 'OBSFisheyeShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 -uniform float radius< - string label = "radius"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.0; -uniform float angle< - string label = "angle"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 360.0; - float step = 0.1; -> = 180.0; -uniform float period< - string label = "period"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 0.5; -uniform float amplitude< - string label = "amplitude"; - string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; - -uniform float center_x< - string label = "center x"; +uniform float center_x_percent< + string label = "Center x percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; -uniform float center_y< - string label = "center y"; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 0.5; - float step = 0.001; -> = 0.25; - -uniform float phase< - string label = "phase"; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power< + string label = "Power"; string widget_type = "slider"; - float minimum = 0.0; - float maximum = 5.0; - float step = 0.001; -> = 1.0; -uniform int animate< - string label = "animate"; - string widget_type = "select"; - int option_0_value = 0; - string option_0_label = "No"; - int option_1_value = 1; - string option_1_label = "Amplitude"; - int option_2_value = 2; - string option_2_label = "Time"; -> = 0; - - -uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." - + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; float4 mainImage(VertData v_in) : TARGET { - float2 center = float2(center_x, center_y); - VertData v_out; - v_out.pos = v_in.pos; - float2 hw = uv_size; - float ar = 1. * hw.y/hw.x; - - v_out.uv = 1. * v_in.uv - center; - - center.x /= ar; - v_out.uv.x /= ar; - - float dist = distance(v_out.uv, center); - if (dist < radius) - { - float percent = (radius-dist)/radius; - float theta = percent * percent * - ( - animate == 1 ? - amplitude * sin(elapsed_time) : - amplitude - ) - * sin(percent * percent / period * radians(angle) + (phase + - ( - animate == 2 ? - elapsed_time : - 0 - ))); - - float s = sin(theta); - float c = cos(theta); - v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); - v_out.uv += (2 * center); - - v_out.uv.x *= ar; - - return image.Sample(textureSampler, v_out.uv); - } - else - { - return image.Sample(textureSampler, v_in.uv); + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv = center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power) * b / tan( b * power); + } else if(power <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv = center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power * 10.0) * b / atan(-power * b * 10.0); } - + return image.Sample(textureSampler, uv); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -45553,35 +37395,30 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomBlurShader { +function Get-OBSFisheyeXyShader { -[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] +[Alias('Set-OBSFisheyeXyShader','Add-OBSFisheyeXyShader')] param( -# Set the samples of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('samples')] -[Int32] -$Samples, -# Set the magnitude of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('magnitude')] +# Set the center_x_percent of OBSFisheyeXyShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] [Single] -$Magnitude, -# Set the speed_percent of OBSZoomBlurShader -[Alias('speed_percent')] -[ComponentModel.DefaultBindingProperty('speed_percent')] -[Int32] -$SpeedPercent, -# Set the ease of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('ease')] -[Management.Automation.SwitchParameter] -$Ease, -# Set the glitch of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('glitch')] -[Management.Automation.SwitchParameter] -$Glitch, -# Set the notes of OBSZoomBlurShader -[ComponentModel.DefaultBindingProperty('notes')] -[String] -$Notes, +$CenterXPercent, +# Set the center_y_percent of OBSFisheyeXyShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Single] +$CenterYPercent, +# Set the power_x of OBSFisheyeXyShader +[Alias('power_x')] +[ComponentModel.DefaultBindingProperty('power_x')] +[Single] +$PowerX, +# Set the power_y of OBSFisheyeXyShader +[Alias('power_y')] +[ComponentModel.DefaultBindingProperty('power_y')] +[Single] +$PowerY, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -45612,106 +37449,68 @@ $UseShaderTime process { -$shaderName = 'zoom_blur' -$ShaderNoun = 'OBSZoomBlurShader' +$shaderName = 'fisheye-xy' +$ShaderNoun = 'OBSFisheyeXyShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 -// https://github.com/Oncorporation/obs-shaderfilter -// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl -//for Media Player Classic HC or BE -//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 -uniform int samples < - string label = "Samples"; +uniform float center_x_percent< + string label = "Center x percent"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 32; -uniform float magnitude< - string label = "Magnitude"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float center_y_percent< + string label = "Center y percent"; string widget_type = "slider"; float minimum = 0.0; - float maximum = 1.0; - float step = 0.001; -> = 0.5; -uniform int speed_percent < - string label = "Speed percent"; + float maximum = 100.0; + float step = 0.01; +> = 50.0; +uniform float power_x< + string label = "Power x"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 0; -uniform bool ease; -uniform bool glitch; -uniform string notes< - string widget_type = "info"; -> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; - -float EaseInOutCircTimer(float t,float b,float c,float d){ - t /= d/2; - if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; - t -= 2; - return c/2 * (sqrt(1 - t*t) + 1) + b; -} - -float Styler(float t,float b,float c,float d,bool ease) -{ - if (ease) return EaseInOutCircTimer(t,0,c,d); - return t; -} + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; +uniform float power_y< + string label = "Power y"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.75; float4 mainImage(VertData v_in) : TARGET { - float speed = speed_percent * 0.01; - - // circular easing variable - float t = 1.0 + sin(elapsed_time * speed); - float b = 0.0; //start value - float c = 2.0; //change value - float d = 2.0; //duration - - if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); - - b = Styler(t, 0, c, d, ease); - float sample_speed = max(samples * b, 1.0); - - float PI = 3.1415926535897932384626433832795;//acos(-1); - float4 c0 = image.Sample(textureSampler, v_in.uv); - - float xTrans = (v_in.uv.x*2)-1; - float yTrans = 1-(v_in.uv.y*2); - - float angle = atan(yTrans/xTrans) + PI; - if (sign(xTrans) == 1) { - angle+= PI; - } - float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); - - float2 currentCoord; - float4 accumulatedColor = float4(0,0,0,0); - - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor = currentColor; - - accumulatedColor = c0/sample_speed; - for(int i = 1; i< sample_speed; i++) { - float currentRadius ; - // Distance to center dependent - currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); - - // Continuous; - // currentRadius = max(0,radius - (0.0004 * i)); - - currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; - currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); - - float4 currentColor = image.Sample(textureSampler, currentCoord); - accumulatedColor += currentColor/sample_speed; - - } - - return accumulatedColor; + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + if (power_x >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.x = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_x) * b / tan( b * power_x)).x; + } else if(power_x <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.x = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_x * 10.0) * b / atan(-power_x * b * 10.0)).x; + } + if (power_y >= 0.0001){ + float b = sqrt(dot(center_pos, center_pos)); + uv.y = (center_pos + normalize(v_in.uv - center_pos) * tan(distance(center_pos, v_in.uv) * power_y) * b / tan( b * power_y)).y; + } else if(power_y <= -0.0001){ + float b; + if (uv_pixel_interval.x < uv_pixel_interval.y){ + b = center_pos.x; + } else { + b = center_pos.y; + } + uv.y = (center_pos + normalize(v_in.uv - center_pos) * atan(distance(center_pos, v_in.uv) * -power_y * 10.0) * b / atan(-power_y * b * 10.0)).y; + } + return image.Sample(textureSampler, uv); } ' @@ -45811,34 +37610,18 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomBlurTransitionShader { +function Get-OBSFlipShader { -[Alias('Set-OBSZoomBlurTransitionShader','Add-OBSZoomBlurTransitionShader')] +[Alias('Set-OBSFlipShader','Add-OBSFlipShader')] param( -# Set the image_a of OBSZoomBlurTransitionShader -[Alias('image_a')] -[ComponentModel.DefaultBindingProperty('image_a')] -[String] -$ImageA, -# Set the image_b of OBSZoomBlurTransitionShader -[Alias('image_b')] -[ComponentModel.DefaultBindingProperty('image_b')] -[String] -$ImageB, -# Set the transition_time of OBSZoomBlurTransitionShader -[Alias('transition_time')] -[ComponentModel.DefaultBindingProperty('transition_time')] -[Single] -$TransitionTime, -# Set the convert_linear of OBSZoomBlurTransitionShader -[Alias('convert_linear')] -[ComponentModel.DefaultBindingProperty('convert_linear')] +# Set the Horizontal of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Horizontal')] [Management.Automation.SwitchParameter] -$ConvertLinear, -# Set the strength of OBSZoomBlurTransitionShader -[ComponentModel.DefaultBindingProperty('strength')] -[Single] -$Strength, +$Horizontal, +# Set the Vertical of OBSFlipShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -45869,82 +37652,32 @@ $UseShaderTime process { -$shaderName = 'zoom_blur_transition' -$ShaderNoun = 'OBSZoomBlurTransitionShader' +$shaderName = 'Flip' +$ShaderNoun = 'OBSFlipShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -//based on https://www.shadertoy.com/view/Ml3XR2 - -uniform texture2d image_a; -uniform texture2d image_b; -uniform float transition_time = 0.5; -uniform bool convert_linear = true; - -//modified zoom blur from http://transitions.glsl.io/transition/b86b90161503a0023231 -uniform float strength< - string label = "Strength (0.3)"; - string widget_type = "slider"; - float minimum = 0.00; - float maximum = 1.50; - float step = 0.01; -> = 0.3; -#define PI 3.141592653589793 - -float Linear_ease(in float begin, in float change, in float duration, in float time) { - return change * time / duration + begin; -} - -float Exponential_easeInOut(in float begin, in float change, in float duration, in float time) { - if (time == 0.0) - return begin; - else if (time == duration) - return begin + change; - time = time / (duration / 2.0); - if (time < 1.0) - return change / 2.0 * pow(2.0, 10.0 * (time - 1.0)) + begin; - return change / 2.0 * (-pow(2.0, -10.0 * (time - 1.0)) + 2.0) + begin; -} - -float Sinusoidal_easeInOut(in float begin, in float change, in float duration, in float time) { - return -change / 2.0 * (cos(PI * time / duration) - 1.0) + begin; -} - -float random(in float3 scale, in float seed) { - return frac(sin(dot(float3(seed, seed, seed), scale)) * 43758.5453 + seed); -} - -float3 crossFade(in float2 uv, in float dissolve) { - return lerp(image_a.Sample(textureSampler, uv).rgb, image_b.Sample(textureSampler, uv).rgb, dissolve); -} - -float4 mainImage(VertData v_in) : TARGET { - float2 texCoord = v_in.uv; - float progress = transition_time; - // Linear interpolate center across center half of the image - float2 center = float2(Linear_ease(0.5, 0.0, 1.0, progress),0.5); - float dissolve = Exponential_easeInOut(0.0, 1.0, 1.0, progress); - - // Mirrored sinusoidal loop. 0->strength then strength->0 - float strength2 = Sinusoidal_easeInOut(0.0, strength, 0.5, progress); - - float3 color = float3(0.0,0.0,0.0); - float total = 0.0; - float2 toCenter = center - texCoord; +// A Simple Flip Shader - /* randomize the lookup values to hide the fixed float of samples */ - float offset = random(float3(12.9898, 78.233, 151.7182), 0.0)*0.5; +uniform bool Horizontal< + string label = "Flip horizontally"; +> = true; +uniform bool Vertical< + string label = "Flip vertically"; +> = true; - for (float t = 0.0; t <= 20.0; t++) { - float percent = (t + offset) / 20.0; - float weight = 1.0 * (percent - percent * percent); - color += crossFade(texCoord + toCenter * percent * strength2, dissolve) * weight; - total += weight; +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + if (Horizontal == true) { + pos.x = 1 - pos.x; } - float4 rgba = float4(color / total, 1.0); - if (convert_linear) - rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); - return rgba; + if (Vertical == true) { + pos.y = 1 - pos.y; + } + + return image.Sample(textureSampler, pos); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -46042,24 +37775,46 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomShader { +function Get-OBSFrostedGlassShader { -[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] +[Alias('Set-OBSFrostedGlassShader','Add-OBSFrostedGlassShader')] param( -# Set the center_x_percent of OBSZoomShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the power of OBSZoomShader -[ComponentModel.DefaultBindingProperty('power')] +# Set the Alpha_Percent of OBSFrostedGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] [Single] -$Power, +$AlphaPercent, +# Set the Amount of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Amount')] +[Single] +$Amount, +# Set the Scale of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Scale')] +[Single] +$Scale, +# Set the Animate of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('Animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the Horizontal_Border of OBSFrostedGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSFrostedGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSFrostedGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the notes of OBSFrostedGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -46090,40 +37845,79 @@ $UseShaderTime process { -$shaderName = 'zoom' -$ShaderNoun = 'OBSZoomShader' +$shaderName = 'frosted_glass' +$ShaderNoun = 'OBSFrostedGlassShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -uniform int center_x_percent< - string label = "center x percent"; +// Frosted Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter + +uniform float Alpha_Percent< + string label = "Alpha Percent"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Amount< + string label = "Amount"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float power< - string label = "power"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.03; +uniform float Scale< + string label = "Scale"; string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1.0; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 5.1; +uniform bool Animate; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.1; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform string notes< + string widget_type = "info"; +> = "Change shader with Scale and Amount, move Border with Border Offset. Alpha is Opacity of overlay."; + +float rand(float2 co) +{ + float scale = Scale; + if (Animate) + scale *= rand_f; + float2 v1 = float2(92.0,80.0); + float2 v2 = float2(41.0,62.0); + return frac(sin(dot(co.xy ,v1)) + cos(dot(co.xy ,v2)) * scale); +} float4 mainImage(VertData v_in) : TARGET { - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; - return image.Sample(textureSampler, uv); + float4 rgba = image.Sample(textureSampler, v_in.uv); + float3 tc = rgba.rgb * Border_Color.rgb; + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; + + if (uv_compare < (Border_Offset - 0.005)) + { + float2 randv = float2(rand(v_in.uv.yx),rand(v_in.uv.yx)); + tc = image.Sample(textureSampler, v_in.uv + (randv*Amount)).rgb; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + tc = image.Sample(textureSampler, v_in.uv).rgb; + } + return lerp(rgba,float4(tc,1.0),(Alpha_Percent * 0.01)); } + ' } $MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 @@ -46221,30 +38015,26 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSZoomXYShader { +function Get-OBSGammaCorrectionShader { -[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] +[Alias('Set-OBSGammaCorrectionShader','Add-OBSGammaCorrectionShader')] param( -# Set the center_x_percent of OBSZoomXYShader -[Alias('center_x_percent')] -[ComponentModel.DefaultBindingProperty('center_x_percent')] -[Int32] -$CenterXPercent, -# Set the center_y_percent of OBSZoomXYShader -[Alias('center_y_percent')] -[ComponentModel.DefaultBindingProperty('center_y_percent')] -[Int32] -$CenterYPercent, -# Set the x_power of OBSZoomXYShader -[Alias('x_power')] -[ComponentModel.DefaultBindingProperty('x_power')] +# Set the Red of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Red')] [Single] -$XPower, -# Set the y_power of OBSZoomXYShader -[Alias('y_power')] -[ComponentModel.DefaultBindingProperty('y_power')] +$Red, +# Set the Green of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Green')] [Single] -$YPower, +$Green, +# Set the Blue of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the notes of OBSGammaCorrectionShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, # The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] [Alias('SceneItemName')] @@ -46275,53 +38065,44 @@ $UseShaderTime process { -$shaderName = 'Zoom_XY' -$ShaderNoun = 'OBSZoomXYShader' +$shaderName = 'gamma_correction' +$ShaderNoun = 'OBSGammaCorrectionShader' if (-not $psBoundParameters['ShaderText']) { $psBoundParameters['ShaderText'] = $ShaderText = ' -// Zoom XY Shader - -// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ - -// The allow for an independent Horizontal and Vertical Zoom. +// Gamma Correction shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter -uniform int center_x_percent< - string label = "center x percent"; +uniform float Red< + string label = "Red"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform int center_y_percent< - string label = "center y percent"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Green< + string label = "Green"; string widget_type = "slider"; - int minimum = 0; - int maximum = 100; - int step = 1; -> = 50; -uniform float x_power< - string label = "x power"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform float Blue< + string label = "Blue"; string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; - -uniform float y_power< - string label = "y power"; - string widget_type = "slider"; - float minimum = 0; - float maximum = 20.0; - float step = 0.001; -> = 1; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; +uniform string notes< + string widget_type = "info"; +> = "Modify Colors to correct for gamma, use equal values for general correction." float4 mainImage(VertData v_in) : TARGET -{ - float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); - float2 uv = v_in.uv; - uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; - uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; - return image.Sample(textureSampler, uv); +{ + float3 gammaRGB = float3(clamp(Red,0.1,10.0),clamp(Green,0.1,10.0),clamp(Blue,0.1,10.0)); + float4 c = image.Sample(textureSampler, v_in.uv); + c.rgb = pow(c.rgb, 1.0 / gammaRGB); + return c; } ' } @@ -46420,3545 +38201,4538 @@ switch -regex ($myVerb) { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSAudioOutputSource { - - - #> - [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] - param( - # The name of the audio device. - # This name or device ID of the audio device that should be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','DeviceID')] - [string] - $AudioDevice, - - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - - # The name of the input. - # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +function Get-OBSGaussianBlurAdvancedShader { - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +[Alias('Set-OBSGaussianBlurAdvancedShader','Add-OBSGaussianBlurAdvancedShader')] +param( +# Set the Directions of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Directions')] +[Single] +$Directions, +# Set the Quality of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Quality')] +[Single] +$Quality, +# Set the Size of OBSGaussianBlurAdvancedShader +[ComponentModel.DefaultBindingProperty('Size')] +[Single] +$Size, +# Set the Mask_Left of OBSGaussianBlurAdvancedShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurAdvancedShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurAdvancedShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurAdvancedShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +process { +$shaderName = 'gaussian-blur-advanced' +$ShaderNoun = 'OBSGaussianBlurAdvancedShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float Directions< + string label = "Directions (16.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) +uniform float Quality< + string label = "Quality (4.0)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 100.0; + float step = 1.0; +> = 4.0; // BLUR QUALITY (Default 4.0 - More is better but slower) +uniform float Size< + string label = "Size (8.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 8.0; // BLUR SIZE (Radius) +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; - } - begin { - # Audio Output sources have an inputKind of 'wasapi_output_capture'. - $inputKind = "wasapi_output_capture" +float4 mainImage(VertData v_in) : TARGET +{ + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - - if (-not $myParameters["AudioDevice"]) { - $myParameters["AudioDevice"] = "default" + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; - - if ($myParameters["AudioDevice"]) { - $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } else { - $Name = $myParameters["Name"] = "AudioOutput" + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - - - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = $inputKind - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id - foreach ($deviceInfo in $possibleDevices) { - if ( - ($deviceInfo.itemName -eq $AudioDevice) -or - ($deviceInfo.ItemValue -eq $AudioDevice) -or - ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or - ($deviceInfo.ItemValue -like "*$AudioDevice*") -or - ($deviceInfo.ItemName -like "*$AudioDevice*") - ) { - $myParameterData["device_id"] = $deviceInfo.itemValue - break + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Scene"] - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSBrowserSource { - - - [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] - param( - # The uri or file path to display. - # If the uri points to a local file, this will be preferred - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url', 'Href','Path','FilePath','FullName')] - [uri] - $Uri, - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, +} - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, - # The css style used to render the browser page. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", +} - # If set, the browser source will shutdown when it is hidden - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGaussianBlurShader { - # If set, the browser source will restart when it is activated. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, +[Alias('Set-OBSGaussianBlurShader','Add-OBSGaussianBlurShader')] +param( +# Set the ViewProj of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the imageSize of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageSize')] +[Single[]] +$ImageSize, +# Set the imageTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('imageTexel')] +[Single[]] +$ImageTexel, +# Set the u_radius of OBSGaussianBlurShader +[Alias('u_radius')] +[ComponentModel.DefaultBindingProperty('u_radius')] +[Int32] +$URadius, +# Set the u_diameter of OBSGaussianBlurShader +[Alias('u_diameter')] +[ComponentModel.DefaultBindingProperty('u_diameter')] +[Int32] +$UDiameter, +# Set the u_texelDelta of OBSGaussianBlurShader +[Alias('u_texelDelta')] +[ComponentModel.DefaultBindingProperty('u_texelDelta')] +[Single[]] +$UTexelDelta, +# Set the elapsed_time of OBSGaussianBlurShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianBlurShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianBlurShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianBlurShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the kernel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernel')] +[String] +$Kernel, +# Set the kernelTexel of OBSGaussianBlurShader +[ComponentModel.DefaultBindingProperty('kernelTexel')] +[Single[]] +$KernelTexel, +# Set the pixel_size of OBSGaussianBlurShader +[Alias('pixel_size')] +[ComponentModel.DefaultBindingProperty('pixel_size')] +[Single] +$PixelSize, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] - [switch] - $RerouteAudio, - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] - [int] - $FramesPerSecond, +process { +$shaderName = 'gaussian-blur' +$ShaderNoun = 'OBSGaussianBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro March 11, 2022 +// OBS Default +uniform float4x4 ViewProj; - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, +// Settings (Shared) +uniform texture2d image; +uniform float2 imageSize; +uniform float2 imageTexel; +uniform int u_radius; +uniform int u_diameter; +uniform float2 u_texelDelta; - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +// Settings (Private) +//uniform float registerkernel[25]; +uniform texture2d kernel; +uniform float2 kernelTexel; +uniform float pixel_size = 1.0; +sampler_state pointClampSampler { + Filter = Point; + AddressU = Clamp; + AddressV = Clamp; +}; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +sampler_state bilinearClampSampler { + Filter = Bilinear; + AddressU = Clamp; + AddressV = Clamp; +}; - } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - $inputKind = "browser_source" - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } +float Gaussian(float x, float o) +{ + float pivalue = 3.1415926535897932384626433832795; + return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2.0 * (o * o))); +} - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } +VertData VSDefault(VertData vert_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv; + return vert_out; +} - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +float4 InternalGaussian(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel) + { + float l_gauss = Gaussian(0.0, 1.0); + float4 l_value = image.Sample(pointClampSampler, p_uv) * l_gauss; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = Gaussian(float(k), uv_pixel_interval.x + uv_pixel_interval.y); + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_gauss += l_g; + } + l_value = l_value * (1.0 / l_gauss); + return l_value; +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +float4 InternalGaussianPrecalculated(float2 p_uv, float2 p_uvStep, int p_radius, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = float2(0, 0); + for (int k = 1; k <= p_radius; k++) { + l_uvoffset += p_uvStep; + float l_g = kernel.Sample(pointClampSampler, p_kernelTexel * k).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + } + return l_value; +} - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } +/*float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, int pixel_size, + texture2d p_image, float2 p_imageTexel, + texture2d p_kernel, float2 p_kernelTexel) + { + if (pixel_size % 2 == 0) { + float4 l_value = image.Sample(pointClampSampler, p_uv) + * kernel.Sample(pointClampSampler, float2(0, 0)).r; + float2 l_uvoffset = p_texel; + float2 l_koffset = p_kernelTexel; + for (int k = 1; k <= pixel_size; k++) { + float l_g = kernel.Sample(pointClampSampler, l_koffset).r; + float4 l_p = image.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g; + float4 l_n = image.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g; + l_value += l_p + l_n; + l_uvoffset += p_texel; + l_koffset += p_kernelTexel; + } + return l_value; + } else { + return InternalGaussianPrecalculated(p_uv, p_image, p_texel, pixel_size, p_kernel, p_kerneltexel);) + } +}*/ - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true - } - if ($uri.Scheme -eq 'File') { - if (Test-Path $uri.AbsolutePath) { - $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true +float4 PSGaussian(VertData vert_in) : TARGET +{ + + float4 color = image.Sample(pointClampSampler, vert_in.uv); + + float intensity = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + + return InternalGaussian(vert_in.uv, uv_offset, int(sqrt((uv_pixel_interval.x * uv_pixel_interval.x) + (uv_pixel_interval.y * uv_pixel_interval.y))), image, uv_scale); + + /* + return InternalGaussianPrecalculated( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ + + /* + return InternalGaussianPrecalculatedNVOptimize( + vert_in.uv, u_texelDelta, u_radius, + image, imageTexel, + kernel, kernelTexel); + */ +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSGaussian(vert_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - else - { - if (Test-Path $uri) { - $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) - $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true - } else { - $myParameterData["url"] = "$uri" + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter } } - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($uri.Segments) { - $uri.Segments[-1] - } elseif ($uri -match '[\\/]') { - @($uri -split '[\\/]')[-1] - } else { - $uri - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } + +} + + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSColorSource { - - - [Alias('Add-OBSColorSource','Get-OBSColorSource')] - param( - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +function Get-OBSGaussianBlurSimpleShader { - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, +[Alias('Set-OBSGaussianBlurSimpleShader','Add-OBSGaussianBlurSimpleShader')] +param( +# Set the Strength of OBSGaussianBlurSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Int32] +$Strength, +# Set the Mask_Left of OBSGaussianBlurSimpleShader +[Alias('Mask_Left')] +[ComponentModel.DefaultBindingProperty('Mask_Left')] +[Single] +$MaskLeft, +# Set the Mask_Right of OBSGaussianBlurSimpleShader +[Alias('Mask_Right')] +[ComponentModel.DefaultBindingProperty('Mask_Right')] +[Single] +$MaskRight, +# Set the Mask_Top of OBSGaussianBlurSimpleShader +[Alias('Mask_Top')] +[ComponentModel.DefaultBindingProperty('Mask_Top')] +[Single] +$MaskTop, +# Set the Mask_Bottom of OBSGaussianBlurSimpleShader +[Alias('Mask_Bottom')] +[ComponentModel.DefaultBindingProperty('Mask_Bottom')] +[Single] +$MaskBottom, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Color, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput +process { +$shaderName = 'gaussian-blur-simple' +$ShaderNoun = 'OBSGaussianBlurSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int Strength< + string label = "Strength (1)"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 1.0; +uniform float Mask_Left< + string label = "Mask left (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Right< + string label = "Mask right (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Top< + string label = "Mask top (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float Mask_Bottom< + string label = "Mask bottom (1.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +float4 mainImage(VertData v_in) : TARGET +{ + if(Strength <= 0) + return image.Sample(textureSampler, v_in.uv); + + if(Mask_Left + Mask_Right > 1.0){ + if(v_in.uv.x > Mask_Left || 1.0 - v_in.uv.x > Mask_Right ){ + return image.Sample(textureSampler, v_in.uv); + } + }else{ + if((v_in.uv.x > Mask_Left) && (1.0-v_in.uv.x > Mask_Right)){ + return image.Sample(textureSampler, v_in.uv); + } + } + if(Mask_Top + Mask_Bottom > 1.0){ + if(v_in.uv.y > Mask_Top || 1.0 - v_in.uv.y > Mask_Bottom){ + return image.Sample(textureSampler, v_in.uv); + } + }else { + if((v_in.uv.y > Mask_Top) && (1.0-v_in.uv.y > Mask_Bottom)){ + return image.Sample(textureSampler, v_in.uv); + } + } + + float Pi = 6.28318530718; // Pi*2 + + float Directions = float(Strength) * 4.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = float(Strength); // BLUR QUALITY (Default 4.0 - More is better but slower) + float Size = float(Strength) * float(Strength); // BLUR SIZE (Radius) + + float4 c = image.Sample(textureSampler, v_in.uv); + float4 oc = c; + float transparent = oc.a; + int count = 1; + float samples = oc.a; + + // Blur calculations + [loop] for( float d=0.0; d 0.0) + c /= samples; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + c.a = transparent / count; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - - } - begin { - $inputKind = "color_source_v3" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $hexChar = [Regex]::new('[0-9a-f]') - $hexColors = @($hexChar.Matches($Color)) + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - switch ($hexColors.Length) { - 8 { - #full rgba - $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') - } - 6 { - #rgb only, assume ff for alpha - $alpha = 0xff - $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') - $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') - $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') - } - 4 { - #short rgba - $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') - } - 3 { - #short rgb, assume f for alpha - $alpha = 0xff - $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') - $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') - $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') - } - 0 { - # No color provided, default to transparent black - $alpha = 0 - $red = 0 - $green = 0 - $blue = 0 + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - $realColor = [uint32]::Parse($hexColor,'HexNumber') - - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "#$hexColor" + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - $myParameterData = [Ordered]@{color=$realColor} - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "color_source_v3" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } - - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 +} - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - } - - } -} +} #.ExternalHelp obs-powershell-Help.xml -function Set-OBSDisplaySource { - - - [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] - param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("monitor")] - [Alias('MonitorNumber','Display','DisplayNumber')] - [int] - $Monitor = 1, +function Get-OBSGaussianExampleShader { - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("capture_cursor")] - [switch] - $CaptureCursor, +[Alias('Set-OBSGaussianExampleShader','Add-OBSGaussianExampleShader')] +param( +# Set the ViewProj of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianExampleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianExampleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianExampleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSGaussianExampleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSGaussianExampleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the initial_image of OBSGaussianExampleShader +[Alias('initial_image')] +[ComponentModel.DefaultBindingProperty('initial_image')] +[String] +$InitialImage, +# Set the before_image of OBSGaussianExampleShader +[Alias('before_image')] +[ComponentModel.DefaultBindingProperty('before_image')] +[String] +$BeforeImage, +# Set the after_image of OBSGaussianExampleShader +[Alias('after_image')] +[ComponentModel.DefaultBindingProperty('after_image')] +[String] +$AfterImage, +# Set the text_color of OBSGaussianExampleShader +[Alias('text_color')] +[ComponentModel.DefaultBindingProperty('text_color')] +[String] +$TextColor, +# Set the max_distance of OBSGaussianExampleShader +[Alias('max_distance')] +[ComponentModel.DefaultBindingProperty('max_distance')] +[Single] +$MaxDistance, +# Set the exp of OBSGaussianExampleShader +[ComponentModel.DefaultBindingProperty('exp')] +[Single] +$Exp, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'gaussian-example' +$ShaderNoun = 'OBSGaussianExampleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; + +/*-------------------------. +| :: Texture and sampler:: | +''-------------------------*/ + + +uniform texture2d initial_image; +sampler_state initial_sampler +{ + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = initial_image; +}; + +uniform texture2d before_image; +sampler_state before_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = before_image; +}; + +uniform texture2d after_image; +sampler_state after_sampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; + texture2d = after_image; +}; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct ColorData { + float4 initial_color : SV_TARGET0; + float4 before_color: SV_TARGET1; + float4 after_color : SV_TARGET2; +}; + +uniform float4 text_color; +uniform float max_distance; +uniform float exp; + +#define PI 3.141592653589793238462643383279502884197169399375105820974 + +VertData mainTransform(VertData v_in) +{ + VertData vert_out = v_in; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} + +float4 grayscale(float4 color) +{ + float grayscale = color.r * 0.3 + color.g * 0.59 + color.b * 0.11; + return float4(grayscale, grayscale, grayscale, color.a); +} + +float4 gaussian(VertData v_in, float angle) +{ + float rad = radians(angle); + float2 dir = float2(sin(rad), cos(rad)) * (uv_pixel_interval * max_distance); + float2 dir_2 = dir * 2.0; + float4 ret = image.Sample(textureSampler, v_in.uv) * 0.375; + + float4 px_away = image.Sample(textureSampler, v_in.uv + dir); + px_away += image.Sample(textureSampler, v_in.uv - dir); + px_away *= 0.25; + + float4 px_2_away = image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away += image.Sample(textureSampler, v_in.uv + dir_2); + px_2_away *= 0.0625; + + return ret + px_away + px_2_away; +} + +ColorData setColorData(VertData v_in): SV_TARGET0 +{ + //string RenderTarget0 = "initial_image"; + ColorData cd;// = (ColorData)0; + cd.initial_color = image.Sample(textureSampler, v_in.uv); + cd.before_color = float4(0.0,0.0,1.0,1.0); + cd.after_color = float4(1.0,0.0,0.0,1.0); + return cd; +} + +float4 blurImageH(VertData v_in) : SV_TARGET1 +{ + //string RenderTarget1 = "before_image"; + //ColorData cd = (ColorData)0; + //cd.initial_color = image.Sample(textureSampler, v_in.uv); + //cd.before_color = float4(0.0,0.0,1.0,1.0);//gaussian(v_in, 0); + return float4(0.0,0.0,1.0,1.0); +} + +float4 blurImageV(VertData v_in) : SV_TARGET2 +{ + //string RenderTarget2 = "after_image"; + //ColorData cd = (ColorData)0; + //cd.after_color = float4(1.0,0.0,0.0,1.0); //gaussian(v_in, 90); + return float4(1.0,0.0,0.0,1.0); +} + +float4 mainImage(VertData v_in) : SV_TARGET0 +{ + float4 color; + ColorData cd;// = (ColorData)0; + + //cd.initial_color = initial_image.Sample(initial_sampler, v_in.uv); + //cd.before_color = before_image.Sample(before_sampler, v_in.uv); + cd.after_color = after_image.Sample(before_sampler, v_in.uv); + + if (max_distance <= 5) { + color = cd.before_color; + } + else { + color = cd.after_color;//image.Sample(textureSampler, v_in.uv); + } + + float4 gray = grayscale(color); + float4 gray_text = grayscale(text_color); + float d = distance(gray.rgb, gray_text.rgb); + if (d <= dot(max_distance, uv_pixel_interval.x * max_distance)){ + float d_c = pow(d*2, exp) / pow(2, exp); + d_c = sin(d_c * PI / 2); + d = pow(1.0 - sin(d * PI/4), exp); + + color.rgb = float3(d,d,d); + } + + return color; +} - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +technique Draw +{ + pass pre + { + vertex_shader = mainTransform(v_in); + pixel_shader = setColorData(v_in); + } - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, + pass b0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageH(v_in); + } - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + pass b1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = blurImageV(v_in); + } + + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Users like 1 indexed, computers like zero-indexed. - $myParameterData["monitor"] = $Monitor - 1 - - if (-not $myParameters["Name"]) { - $myParameters["Name"] = "Display $($Monitor)" + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "monitor_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } +} - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } +} - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name - } - - } -} #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMarkdownSource { - - - [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] - param( - # The markdown text, or the path to a markdown file - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Markdown, +function Get-OBSGaussianSimpleShader { - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, +[Alias('Set-OBSGaussianSimpleShader','Add-OBSGaussianSimpleShader')] +param( +# Set the ViewProj of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSGaussianSimpleShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSGaussianSimpleShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSGaussianSimpleShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSGaussianSimpleShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSGaussianSimpleShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSGaussianSimpleShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSGaussianSimpleShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSGaussianSimpleShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSGaussianSimpleShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the samples of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the LOD of OBSGaussianSimpleShader +[ComponentModel.DefaultBindingProperty('LOD')] +[Int32] +$LOD, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, - # The css style used to render the markdown. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", +process { +$shaderName = 'gaussian-simple' +$ShaderNoun = 'OBSGaussianSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Single-pass gaussian blur - fast shader modified by Charles Fettinger for use with obs-shaderfilter 7/2020 v.01 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/ltScRG Converted inspiration - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, +//Section to converting GLSL to HLSL - can delete +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, +/* +**Shaders have these variables pre loaded by the plugin** +**this section can be deleted** - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; +*/ - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +// 16x acceleration of https://www.shadertoy.com/view/4tSyzy +// by applying gaussian at intermediate MIPmap level. - } - begin { - $inputKind = "markdown_source" - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters +uniform int samples< + string label = "Samples"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 25; + int step = 1; +> = 16; +uniform int LOD< + string label = "LOD"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 25; + int step = 1; +> = 2; // gaussian done on MIPmap at scale LOD - $IsGet = $MyInvocation.InvocationName -like "Get-*" - $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +float gaussian(vec2 i) +{ + float sigma = (float(samples) * .25); + return exp(-.5 * dot(i /= sigma, i)) / (6.28 * sigma * sigma); +} - if ( - $IsGet -or - ($NoVerb -and -not $NonNameParameters) - ) { - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - return - } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight - } +vec4 blur(vec2 U, vec2 scale) +{ + vec4 O = vec4(0,0,0,0); + int sLOD = (1 << LOD); // tile size = 2^LOD + int s = samples / sLOD; + + for (int i = 0; i < s * s; i++) + { + vec2 d = vec2(i % s, i / s) * float(sLOD) - float(samples) * 0.5; + O += gaussian(d) * image.SampleLevel(textureSampler, U + (scale * gaussian(d)), float(LOD)); + //O += gaussian(d) * image.Sample(textureSampler, U + i * d * float(LOD)); + //O += image.Sample(textureSampler, U + gaussian(d) * float(LOD)); + } + + return O / O.a; +} - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +float4 mainImage(VertData v_in) : TARGET +{ + float2 iResolution = uv_scale;//uv_size * uv_scale + uv_offset; + //float2 iResolution = 1 - v_in.uv + 1.0; + //float4 rgba = image.SampleLevel(textureSampler, v_in.uv * uv_scale + uv_offset,4.0); + return blur(v_in.uv / iResolution, 1.0 / iResolution); + //return rgba; +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } - $markdownAsUri = $null - if ($Markdown -like '*.md') { - $markdownAsUri = $markdown -as [uri] - if ($markdownAsUri.Scheme -eq 'File') { - $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["markdown_source"] = 1 + + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } else { - + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - $myParameterData["text"] = $Markdown - $myParameterData["markdown_source"] = 0 + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($markdownAsUri.Segments) { - $markdownAsUri.Segments[-1] - } elseif ($markdownAsUri -match '[\\/]') { - @($markdownAsUri -split '[\\/]')[-1] - } elseif ($markdownAsUri) { - $markdownAsUri - } else { - "Markdown" + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } - - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = "markdown_source" - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + continue nextParameter + } } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - return } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaSource { - - - [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file')] - [string] - $FilePath, - # If set, the source will close when it is inactive. - # By default, this will be set to true. - # To explicitly set it to false, use -CloseWhenInactive:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("close_when_inactive")] - [switch] - $CloseWhenInactive, +} - # If set, the source will automatically restart. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("looping")] - [Alias('Looping')] - [switch] - $Loop, - # If set, will use hardware decoding, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("hw_decode")] - [Alias('HardwareDecoding','hw_decode')] - [switch] - $UseHardwareDecoding, +} - # If set, will clear the output on the end of the media. - # If this is set to false, the media will freeze on the last frame. - # This is set to true by default. - # To explicitly set to false, use -ClearMediaEnd:$false - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("clear_on_media_end")] - [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] - [switch] - $ClearOnMediaEnd, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGbCameraShader { - # Any FFMpeg demuxer options. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ffmpeg_options")] - [Alias('FFMpegOptions', 'FFMpeg_Options')] - [string] - $FFMpegOption, +[Alias('Set-OBSGbCameraShader','Add-OBSGbCameraShader')] +param( +# Set the pixelSize of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('pixelSize')] +[Single] +$PixelSize, +# Set the dither_factor of OBSGbCameraShader +[Alias('dither_factor')] +[ComponentModel.DefaultBindingProperty('dither_factor')] +[Single] +$DitherFactor, +# Set the alternative_bayer of OBSGbCameraShader +[Alias('alternative_bayer')] +[ComponentModel.DefaultBindingProperty('alternative_bayer')] +[Management.Automation.SwitchParameter] +$AlternativeBayer, +# Set the brightness of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('brightness')] +[Single] +$Brightness, +# Set the contrast of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('contrast')] +[Single] +$Contrast, +# Set the gamma of OBSGbCameraShader +[ComponentModel.DefaultBindingProperty('gamma')] +[Single] +$Gamma, +# Set the color_1 of OBSGbCameraShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the color_2 of OBSGbCameraShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the color_3 of OBSGbCameraShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the color_4 of OBSGbCameraShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, +process { +$shaderName = 'gb-camera' +$ShaderNoun = 'OBSGbCameraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +/* + * ------------------------------------------------------------ + * "THE BEERWARE LICENSE" (Revision 42): + * maple wrote this code. As long as you retain this + * notice, you can do whatever you want with this stuff. If we + * meet someday, and you think this stuff is worth it, you can + * buy me a beer in return. + * ------------------------------------------------------------ + * from https://www.shadertoy.com/view/3tSXRh + * adopted for OBS by Exeldro + * ------------------------------------------------------------ + */ - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, +uniform float pixelSize< + string label = "Pixel Size"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 50.0; + float step = 0.1; +> = 3.0; - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +uniform float dither_factor< + string label = "Dither Factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; +uniform bool alternative_bayer; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +uniform float brightness< + string label = "Brightness"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float contrast< + string label = "Contrast"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float gamma< + string label = "Gamma"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.6; - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - - } - $InputKind = "ffmpeg_source" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' +uniform float4 color_1 = {0.18, 0, 0.18, 1.0}; +uniform float4 color_2 = {0.37, 0.15, 0.47, 1.0}; +uniform float4 color_3 = {0.97, 0.56, 0.12, 1.0}; +uniform float4 color_4 = {0.97, 0.94, 0.53, 1.0}; - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +// quantize coords to low resolution +float2 pixelize(float2 uv, float2 pixelSize) { + float2 factor = pixelSize / uv_size; + return floor(uv / factor) * factor; +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +float3 colorLUT(float3 color) { + float gray = color.r*0.3 + color.g*0.59 + color.b*0.11; + if(gray < 0.25) + return color_1.rgb; + if(gray < 0.50) + return color_2.rgb; + if(gray < 0.75) + return color_3.rgb; + return color_4.rgb; +} - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } +// adjust brightness, contrast and gamma levels of a color +float3 levels(float3 color, float brightness, float contrast, float3 gamma) { + float3 value = (color - 0.5) * contrast + 0.5; + value = clamp(value + brightness, 0.0, 1.0); + return clamp(float3(pow(abs(value.r), gamma.x),pow(abs(value.g), gamma.y),pow(abs(value.b), gamma.z)), 0.0, 1.0); +} +float3 levels(float3 color, float brightness, float contrast, float gamma) { + return levels(color, brightness, contrast, float3(gamma, gamma, gamma)); +} - if ((Test-Path $FilePath)) { - $FilePathItem = Get-Item -Path $FilePath - $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' - } +// applies the dithering filter to a color map +float3 dither8x8(float2 coord, float3 color, float2 pixelSize) { + // reduces pixel space to the selected pixel size + float2 pixelCoord = floor((coord * uv_size) / pixelSize + float2(0.5, 0.5)); + + // applies the bayer matrix filter to the color map + pixelCoord = pixelCoord - 8.0 * floor(pixelCoord/8.0); + int index = int(pixelCoord.x + (pixelCoord.y * 8.0)); + float bayer; + if (alternative_bayer){ +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ + 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ + 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ + 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ + 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; + } else { +#ifdef OPENGL + const int[64] bayer8 = int[64]( +#else + const int bayer8[64] = { +#endif + 0, 48, 12, 60, 3, 51, 15, 63, + 32, 16, 44, 28, 35, 19, 47, 31, + 8, 56, 4, 52, 11, 59, 7, 55, + 40, 24, 36, 20, 43, 27, 39, 23, + 2, 50, 14, 62, 1, 49, 13, 61, + 34, 18, 46, 30, 33, 17, 45, 29, + 10, 58, 6, 54, 9, 57, 5, 53, + 42, 26, 38, 22, 41, 25, 37, 21 +#ifdef OPENGL + ); +#else + }; +#endif + bayer = (bayer8[index]-31.0)/32.0; + } + float3 bayerColor = (color + float3(bayer,bayer,bayer) * (dither_factor / 8.0)); + // limits it to the selected palette + color = colorLUT(bayerColor); - + return color; +} - if ($myParameters['InputSettings']) { - $keys = - @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { - $myParameters['InputSettings'].Keys - } else { - foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { - $prop.Name - } - }) +float4 mainImage(VertData v_in) : TARGET +{ + float2 texcoord = pixelize(v_in.uv, float2(pixelSize,pixelSize)); + texcoord = clamp(texcoord, 0.001, 1.0); + float4 c = image.Sample(textureSampler, texcoord); + float3 color = c.rgb; + + color = levels(color, brightness, contrast, float3(gamma, gamma, gamma)); + + color = dither8x8(texcoord, color, float2(pixelSize,pixelSize)); + + return float4(color.r, color.g, color.b, c.a); +} - foreach ($key in $keys) { - $myParameterData[$key] = $myParameters['InputSettings'].$key +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - - $myParameterData.remove('inputSettings') + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - if (-not $Name) { - - $Name = $myParameters["Name"] = - if ($FilePathItem.Name) { - $FilePathItem.Name - } else { - "Media" + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - + continue nextParameter + } } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - return } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - - # Otherwise, if we had a result - if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSoundCloudSource { - - - [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] - param( - # The uri to display. This must point to a SoundCloud URL. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Url','SoundCloudUri','SoundCloudUrl')] - [uri] - $Uri, - - # If set, will not autoplay. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoAutoPlay, - - # If set, will not display album artwork. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoArtwork, - - # If set, will not display play count. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoPlayCount, - - # If set, will not display uploader info. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $NoUploaderInfo, - - # If provided, will start playing at a given track number. - [Parameter(ValueFromPipelineByPropertyName)] - [int] - $TrackNumber, - - # If set, will show a share link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowShare, - - # If set, will show a download link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowDownload, - - # If set, will show a buy link. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $ShowBuy, - - # The color used for the SoundCloud audio bars and buttons. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Color, - - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, - # The css style used to render the browser page. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("css")] - [string] - $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", +} - # If set, the browser source will shutdown when it is hidden - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shutdown")] - [switch] - $ShutdownWhenHidden, - # If set, the browser source will restart when it is activated. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("restart_when_active")] - [switch] - $RestartWhenActived, +} - # If set, audio from the browser source will be rerouted into OBS. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("reroute_audio")] - [switch] - $RerouteAudio, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlassShader { - # If provided, the browser source will render at a custom frame rate. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fps")] - [Alias('FPS')] - [int] - $FramesPerSecond, +[Alias('Set-OBSGlassShader','Add-OBSGlassShader')] +param( +# Set the Alpha_Percent of OBSGlassShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Offset_Amount of OBSGlassShader +[Alias('Offset_Amount')] +[ComponentModel.DefaultBindingProperty('Offset_Amount')] +[Single] +$OffsetAmount, +# Set the xSize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('xSize')] +[Int32] +$XSize, +# Set the ySize of OBSGlassShader +[ComponentModel.DefaultBindingProperty('ySize')] +[Int32] +$YSize, +# Set the Reflection_Offset of OBSGlassShader +[Alias('Reflection_Offset')] +[ComponentModel.DefaultBindingProperty('Reflection_Offset')] +[Int32] +$ReflectionOffset, +# Set the Horizontal_Border of OBSGlassShader +[Alias('Horizontal_Border')] +[ComponentModel.DefaultBindingProperty('Horizontal_Border')] +[Management.Automation.SwitchParameter] +$HorizontalBorder, +# Set the Border_Offset of OBSGlassShader +[Alias('Border_Offset')] +[ComponentModel.DefaultBindingProperty('Border_Offset')] +[Single] +$BorderOffset, +# Set the Border_Color of OBSGlassShader +[Alias('Border_Color')] +[ComponentModel.DefaultBindingProperty('Border_Color')] +[String] +$BorderColor, +# Set the Glass_Color of OBSGlassShader +[Alias('Glass_Color')] +[ComponentModel.DefaultBindingProperty('Glass_Color')] +[String] +$GlassColor, +# Set the notes of OBSGlassShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, then "SoundCloud" will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +process { +$shaderName = 'glass' +$ShaderNoun = 'OBSGlassShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Glass shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; +uniform float Offset_Amount< + string label = "Offset Amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.8; +uniform int xSize< + string label = "x Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int ySize< + string label = "y Size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 8; +uniform int Reflection_Offset< + string label = "Reflection Offset"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 2; +uniform bool Horizontal_Border; +uniform float Border_Offset< + string label = "Border Offset"; + string widget_type = "slider"; + float minimum = -0.01; + float maximum = 1.01; + float step = 0.01; +> = 0.5; +uniform float4 Border_Color = {.8,.5,1.0,1.0}; +uniform float4 Glass_Color; +uniform string notes< + string widget_type = "info"; +> = "xSize, ySize are for distortion. Offset Amount and Reflection Offset change glass properties. Alpha is Opacity of overlay."; - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +float mod(float a, float b){ + float d = a / b; + return (d-floor(d))*b; +} +float4 mainImage(VertData v_in) : TARGET +{ + - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + int xSubPixel = int(mod((v_in.uv.x * uv_size.x) , float(clamp(xSize,1,100)))); + int ySubPixel = int(mod((v_in.uv.y * uv_size.y) , float(clamp(ySize,1,100)))); + float2 offsets = float2(Offset_Amount * xSubPixel / uv_size.x, Offset_Amount * ySubPixel / uv_size.y); + float2 uv = v_in.uv + offsets; + float2 uv2 = float2(uv.x + (Reflection_Offset / uv_size.x),uv.y + (Reflection_Offset / uv_size.y)); + + float4 rgba = image.Sample(textureSampler, v_in.uv); + float4 rgba_output = float4(rgba.rgb * Border_Color.rgb, rgba.a); + rgba = image.Sample(textureSampler, uv); + float4 rgba_glass = image.Sample(textureSampler, uv2); + + float uv_compare = v_in.uv.x; + if (Horizontal_Border) + uv_compare = v_in.uv.y; + + if (uv_compare < (Border_Offset - 0.005)) + { + rgba_output = (rgba + rgba_glass) *.5 * Glass_Color; + } + else if (uv_compare >= (Border_Offset + 0.005)) + { + rgba_output = image.Sample(textureSampler, v_in.uv); + } + return lerp(rgba,rgba_output,(Alpha_Percent * 0.01)); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - # Browser Sources are built into OBS. Their input kind is browser_source. - # Sound Cloud Sources are really Browser Sources. - $inputKind = "browser_source" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { - Write-Error "URI must be from SoundCloud.com" - return + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } } - if ($uri.Query) { - $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } | - Where-Object { - $_.Settings['LocalFile'] -like '*.SoundCloud.*' - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings - } - $videoSettings = $script:CachedOBSVideoSettings - - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName - } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { +} - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } +} - if ($fps -and $fps -ne 30) { - $myParameterData["custom_fps"] = $true - } + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlitchAnalogShader { - $MyObsPowerShellPath = if ($home) { - Join-Path $home ".obs-powershell" - } +[Alias('Set-OBSGlitchAnalogShader','Add-OBSGlitchAnalogShader')] +param( +# Set the scan_line_jitter_displacement of OBSGlitchAnalogShader +[Alias('scan_line_jitter_displacement')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_displacement')] +[Single] +$ScanLineJitterDisplacement, +# Set the scan_line_jitter_threshold_percent of OBSGlitchAnalogShader +[Alias('scan_line_jitter_threshold_percent')] +[ComponentModel.DefaultBindingProperty('scan_line_jitter_threshold_percent')] +[Int32] +$ScanLineJitterThresholdPercent, +# Set the vertical_jump_amount of OBSGlitchAnalogShader +[Alias('vertical_jump_amount')] +[ComponentModel.DefaultBindingProperty('vertical_jump_amount')] +[Single] +$VerticalJumpAmount, +# Set the vertical_speed of OBSGlitchAnalogShader +[Alias('vertical_speed')] +[ComponentModel.DefaultBindingProperty('vertical_speed')] +[Single] +$VerticalSpeed, +# Set the horizontal_shake of OBSGlitchAnalogShader +[Alias('horizontal_shake')] +[ComponentModel.DefaultBindingProperty('horizontal_shake')] +[Single] +$HorizontalShake, +# Set the color_drift_amount of OBSGlitchAnalogShader +[Alias('color_drift_amount')] +[ComponentModel.DefaultBindingProperty('color_drift_amount')] +[Single] +$ColorDriftAmount, +# Set the color_drift_speed of OBSGlitchAnalogShader +[Alias('color_drift_speed')] +[ComponentModel.DefaultBindingProperty('color_drift_speed')] +[Single] +$ColorDriftSpeed, +# Set the pulse_speed_percent of OBSGlitchAnalogShader +[Alias('pulse_speed_percent')] +[ComponentModel.DefaultBindingProperty('pulse_speed_percent')] +[Int32] +$PulseSpeedPercent, +# Set the alpha_percent of OBSGlitchAnalogShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the rotate_colors of OBSGlitchAnalogShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGlitchAnalogShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Replace_Image_Color of OBSGlitchAnalogShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSGlitchAnalogShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGlitchAnalogShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSGlitchAnalogShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - $ThisSoundCloudSourceFileName = - if ($name) { - "${name}.SoundCloudSource.html" - } else { - "SoundCloudSource.html" - } - $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName - +process { +$shaderName = 'glitch_analog' +$ShaderNoun = 'OBSGlitchAnalogShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// analog glitch shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float scan_line_jitter_displacement< + string label = "Scan line jitter"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 33.0; // (displacement, threshold) +uniform int scan_line_jitter_threshold_percent< + string label = "scan line jitter threshold percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 95; +uniform float vertical_jump_amount< + string label = "Vertical jump amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float vertical_speed< + string label = "Vertical speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform float horizontal_shake< + string label = "Horizontal shake"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_amount< + string label = "Color drift amount"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>; +uniform float color_drift_speed< + string label = "Color drift speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +>;// (amount, speed) +uniform int pulse_speed_percent< + string label = "Pulse speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int alpha_percent< + string label = "Aplha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> ="play with settings!"; - $soundCloudSrc = @( - "https://w.soundcloud.com/player/?url=" - $Uri - "&" - @( - if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} - if ($color) { "color=$color" -replace "\#",'%23'} - if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} - if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } - if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} - if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } - if ($ShowDownload) { "download=true"} else { "download=false" } - if ($ShowBuy) { "buying=true"} else { "buying=false" } - if ($ShowShare) { "sharing=true"} else { "sharing=false" } - ) -join '&' - ) -join '' - - $soundCloudWidget = @( - "" - "" - "" - "" - "" - ) -join ' ' - $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force - - $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' - $myParameterData["is_local_file"] = $true +float nrand(float x, float y) +{ + float value = dot(float2(x, y), float2(12.9898 , 78.233 )); + return frac(sin(value) * 43758.5453); +} - if (-not $Name) { - $Name = $myParameters['Name'] = 'SoundCloud' - } +float4 mainImage(VertData v_in) : TARGET +{ + float speed = pulse_speed_percent * 0.01; + float alpha = alpha_percent * 0.01; + float scan_line_jitter_threshold = scan_line_jitter_threshold_percent * 0.01; + float u = v_in.uv.x; + float v = v_in.uv.y; + float t = sin(elapsed_time * speed) * 2 - 1; + float4 rgba = image.Sample(textureSampler, v_in.uv); - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + // Scan line jitter + float jitter = nrand(v, t) * 2 - 1; + jitter *= step(scan_line_jitter_threshold, abs(jitter)) * scan_line_jitter_displacement; + + // Vertical jump + float jump = lerp(v, frac(v + (t * vertical_speed)), vertical_jump_amount); + + // Horizontal shake + float shake = ((t * (u + rand_f)/2) - 0.5) * horizontal_shake; + + //// Color drift + float drift = sin(jump + color_drift_speed) * color_drift_amount; + + float2 src1 = float2(rgba.x, rgba.z) * clamp(frac(float2(u + jitter + shake, jump)), -10.0, 10.0); + float2 src2 = float2(rgba.y, rgba.w) * frac(float2(u + jitter + shake + drift, jump)); + + if(rotate_colors) + { + // get general time number between 0 and 4 + float tx = (t + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls + //(min(tx, 2.0) * 0.5) range between 0-2 converted to 0-1-0 + src1.x = lerp(src1.x, rgba.x, clamp((min(tx, 2.0) * 0.5),0.0,0.5)); + //((min(max(1.0, tx),3.0) - 1) * 0.5) range between 1-3 converted to 0-1-0 + src2.x = lerp(src2.x, rgba.y, clamp(((min(max(1.0, tx),3.0) - 1) * 0.5),0.0,0.5)); + //((min(2.0, tx) -2) * 0.5) range between 2 and 4 converted to 0-1-0 + src1.y = lerp(src1.y, rgba.z, clamp(((min(2.0, tx) -2) * 0.5),0.0,0.5)); + + } + + float4 color = rgba; + float4 original_color = color; + rgba = float4(src1.x, src2.x, src1.y, alpha); + + if (Apply_To_Alpha_Layer) + { + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color, alpha); + } + + if (Apply_To_Specific_Color) + { + color = original_color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, alpha); + } + + return rgba; +} - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - return + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } - - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + continue nextParameter } } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSwitchSource { - - - [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('Sources')] - [string[]] - $SourceList, - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." - } - return $true - })] - - [Alias('SelectIndex','SelectName')] - $Select, + } - # If set, the list of sources will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - # If set, will switch between sources. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch")] - [switch] - $TimeSwitch, + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - # The interval between sources - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_between")] - [timespan] - $Interval, + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} - # The duration between sources that are switching at a time. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("time_switch_duration")] - [timespan] - $Duration, +} - # The item that will be switched in a TimeSwitch, after -Duration and -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $TimeSwitchTo = "Next", - # If set, will switch on the underlying source's media state events. - # Sources will be displayed for a -Duration. - # No source wil be displayed for an -Interval. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("media_state_switch")] - [switch] - $MediaStateSwitch, +} - # The change in media state that should trigger a switch - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] - $MediaStateChange, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlitchPeriodicShader { - # When the source switcher is trigger by media end, this determines the next source that will be switched to. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet("None","Next","Previous","First","Last","Random")] - [string] - $MediaSwitchTo = "Next", +[Alias('Set-OBSGlitchPeriodicShader','Add-OBSGlitchPeriodicShader')] +param( +# Set the PERI of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('PERI')] +[Single] +$PERI, +# Set the DURA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('DURA')] +[Single] +$DURA, +# Set the AMPL of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('AMPL')] +[Single] +$AMPL, +# Set the SCRA of OBSGlitchPeriodicShader +[ComponentModel.DefaultBindingProperty('SCRA')] +[Single] +$SCRA, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The name of the transition between sources. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $TransitionName, - # The properties sent to the transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $TransitionProperty, +process { +$shaderName = 'glitch-periodic' +$ShaderNoun = 'OBSGlitchPeriodicShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Created by Éric Nicolas (ccjmne) for use with obs-shaderfilter 12/2025 +// Port of: https://www.shadertoy.com/view/WfVfDh +// Originally forked from: https://www.shadertoy.com/view/MtXBDs - # The name of the transition used to show a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $ShowTransition, +#define PI 3.14159265359 - # The properties sent to the show transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $ShowTransitionProperty, +/* For visual explanation of the paramters, see */ +/* https://www.desmos.com/calculator/vezu1wyqma */ +/* */ +/* Period How often a glitch occurs (in seconds) 0–? */ +/* Duration How long a glitch lasts (in seconds) 0–Period */ +/* Amplitude How intense a glitch is 0–1 */ +/* Scratchiness How jittery a glitch is 0–1 */ - # The transition used to hide a source. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - if (-not $script:OBSTransitionKinds) { - $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' - } - - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $HideTransition, +uniform float PERI< + string label = "Period"; + string widget_type = "slider"; + float minimum = 1.; + float maximum = 60.; + float step = 1.; +> = 6.; - # The properties sent to the hide transition. - # Notice: this current requires confirmation in the UI. - [Parameter(ValueFromPipelineByPropertyName)] - [PSObject] - $HideTransitionProperty, +uniform float DURA< + string label = "Duration"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 60.; + float step = .01; +> = .5; - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, +uniform float AMPL< + string label = "Amplitude"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .15; - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName','SourceName')] - [string] - $Name, +uniform float SCRA< + string label = "Scratchiness"; + string widget_type = "slider"; + float minimum = 0.; + float maximum = 1.; + float step = .01; +> = .2; - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); +} - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +float randomRange(in float2 seed, in float lo, in float hi) { + return lo + random2d(seed) * (hi - lo); +} +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); +} - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} - } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters +float4 mainImage(VertData v_in): TARGET { + float time = floor(elapsed_time * SCRA * 60.); + float2 uv = v_in.uv; - } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - - } - $InputKind = "source_switcher" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + // Periodic intermittence + float AMP = AMPL * (cos(2. * PI * max(0., (mod(-elapsed_time / PERI, 1.) - 1.) * PERI / DURA + 1.)) * -.5 + .5); - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return - } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + float4 outCol = image.Sample(textureSampler, uv); + + // Randomly offset slices horizontally + float offsetMax = AMP / 2.; + for (float i = 0.; i < 10. * AMP; i += 1.) { + float sliceY = random2d( float2(time, 2345. + i)); + float sliceH = random2d( float2(time, 9035. + i)) * .25; + float offsetH = randomRange(float2(time, 9625. + i), -offsetMax, offsetMax); + float2 uvOff = uv; + uvOff.x += offsetH; + if (insideRange(uv.y, sliceY, frac(sliceY + sliceH)) == 1.) { + outCol = image.Sample(textureSampler, uvOff); } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + } - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + // Slightly offset one entire channel + offsetMax = AMP / 6.; + float2 colOff = float2( + randomRange(float2(time, 9545.), -offsetMax, offsetMax), + randomRange(float2(time, 7205.), -offsetMax, offsetMax) + ); + float rnd = random2d(float2(time , 9545.)); + if (rnd < .33) outCol.r = image.Sample(textureSampler, uv + colOff).r; + else if (rnd < .66) outCol.g = image.Sample(textureSampler, uv + colOff).g; + else outCol.b = image.Sample(textureSampler, uv + colOff).b; - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - if ($myParameters[$parameter.Name] -is [timespan]) { - $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds - } + return outCol; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - - $selectedIndex = -1 - $sourcesObject = @( - $currentIndex = -1 - foreach ($sourceName in $SourceList) { - $currentIndex++ - $selected = ($null -ne $Select) -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [string] -and - ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) - ) - ) - if ($selected) { - $selectedIndex = $currentIndex + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} - } - ) - - if ($sourcesObject) { - $myParameterData['sources'] = $sourcesObject - if ($selectedIndex -gt 0) { - $myParameterData["current_index"] = $selectedIndex - } + continue nextParameter + } } - elseif ($Select -is [int]) { - $myParameterData['current_index'] = $Select + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - - if ($TransitionName) { - if ($TransitionName -notlike '*_transition') { - $TransitionName = "${TransitionName}_transition" + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - $myParameterData["transition"] = $TransitionName - } - - if ($TransitionProperty) { - $myParameterData["transition_properties"] = $TransitionProperty } - if ($ShowTransition) { - if ($ShowTransition -notlike '*_transition') { - $ShowTransition = "${ShowTransition}_transition" - } - $myParameterData["show_transition"] = $ShowTransition + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($ShowTransitionProperty) { - $myParameterData["show_transition_properties"] = $ShowTransitionProperty + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($HideTransition) { - if ($HideTransition -notlike '*_transition') { - $HideTransition = "${HideTransition}_transition" - } - $myParameterData["hide_transition"] = $ShowTransition + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - if ($HideTransitionProperty) { - $myParameterData["hide_transition_properties"] = $HideTransitionProperty - } +} - if ($TimeSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["time_switch_to"] = $vvi - break - } - } - } - if ($MediaSwitchTo) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_state_switch_to"] = $vvi - break - } - } - } +} - if ($MediaStateChange) { - $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues - for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { - if ($TimeSwitchTo -eq $validValues[$vvi]) { - $myParameterData["media_switch_state"] = $vvi - break - } - } - } - if (-not $Name) { - $Name = $myParameters["Name"] = "Source Switcher" - } +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGlitchShader { - +[Alias('Set-OBSGlitchShader','Add-OBSGlitchShader')] +param( +# Set the AMT of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('AMT')] +[Single] +$AMT, +# Set the SPEED of OBSGlitchShader +[ComponentModel.DefaultBindingProperty('SPEED')] +[Single] +$SPEED, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +process { +$shaderName = 'glitch' +$ShaderNoun = 'OBSGlitchShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/MtXBDs +//inputs +uniform float AMT< + string label = "AMT"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.2; //0 - 1 glitch amount +uniform float SPEED< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.6; //0 - 1 speed - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat +//2D (returns 0 - 1) +float random2d(float2 n) { + return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453); +} + +float randomRange (in float2 seed, in float min, in float max) { + return min + random2d(seed) * (max - min); +} + +// return 1 if v inside 1d range +float insideRange(float v, float bottom, float top) { + return step(bottom, v) - step(top, v); +} + + + +float4 mainImage(VertData v_in) : TARGET +{ + + float time = floor(elapsed_time * SPEED * 60.0); + float2 uv = v_in.uv; + + //copy orig + float4 outCol = image.Sample(textureSampler, uv); + + //randomly offset slices horizontally + float maxOffset = AMT/2.0; + for (float i = 0.0; i < 10.0 * AMT; i += 1.0) { + float sliceY = random2d(float2(time , 2345.0 + float(i))); + float sliceH = random2d(float2(time , 9035.0 + float(i))) * 0.25; + float hOffset = randomRange(float2(time , 9625.0 + float(i)), -maxOffset, maxOffset); + float2 uvOff = uv; + uvOff.x += hOffset; + if (insideRange(uv.y, sliceY, frac(sliceY+sliceH)) == 1.0 ){ + outCol = image.Sample(textureSampler, uvOff); + } + } + + //do slight offset on one entire channel + float maxColOffset = AMT/6.0; + float rnd = random2d(float2(time , 9545.0)); + float2 colOffset = float2(randomRange(float2(time , 9545.0),-maxColOffset,maxColOffset), + randomRange(float2(time , 7205.0),-maxColOffset,maxColOffset)); + if (rnd < 0.33){ + outCol.r = image.Sample(textureSampler, uv + colOffset).r; + + }else if (rnd < 0.66){ + outCol.g = image.Sample(textureSampler, uv + colOffset).g; + + } else{ + outCol.b = image.Sample(textureSampler, uv + colOffset).b; + } + + return outCol; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - return + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - if ($sceneItem) { - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } + +} + + +} + #.ExternalHelp obs-powershell-Help.xml -function Set-OBSVLCSource { - - - [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] - param( - # The path to the media file. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('FullName','LocalFile','local_file','Playlist')] - [string[]] - $FilePath, +function Get-OBSGlowShader { - # What to select in the playlist. - # If a number is provided, this will select an index. - # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. - # If an `[IO.FileInfo]` is provided, this will be the exact file. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateScript({ - $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." - } - return $true - })] - - [Alias('SelectIndex','SelectName')] - $Select, +[Alias('Set-OBSGlowShader','Add-OBSGlowShader')] +param( +# Set the glow_percent of OBSGlowShader +[Alias('glow_percent')] +[ComponentModel.DefaultBindingProperty('glow_percent')] +[Int32] +$GlowPercent, +# Set the blur of OBSGlowShader +[ComponentModel.DefaultBindingProperty('blur')] +[Int32] +$Blur, +# Set the min_brightness of OBSGlowShader +[Alias('min_brightness')] +[ComponentModel.DefaultBindingProperty('min_brightness')] +[Int32] +$MinBrightness, +# Set the max_brightness of OBSGlowShader +[Alias('max_brightness')] +[ComponentModel.DefaultBindingProperty('max_brightness')] +[Int32] +$MaxBrightness, +# Set the pulse_speed of OBSGlowShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGlowShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the notes of OBSGlowShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # If set, will shuffle the playlist - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("shuffle")] - [switch] - $Shuffle, - # If set, the playlist will loop. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("loop")] - [Alias('Looping')] - [switch] - $Loop, +process { +$shaderName = 'glow' +$ShaderNoun = 'OBSGlowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int glow_percent< + string label = "Glow percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 10; +uniform int blur< + string label = "Blur"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 1; +uniform int min_brightness< + string label = "Min brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 27; +uniform int max_brightness< + string label = "Max brightness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform int pulse_speed< + string label = "Pulse speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform string notes< + string widget_type = "info"; +> = "''ease'' - makes the animation pause at the begin and end for a moment,''glow_percent'' - how much brightness to add (recommend 0-100). ''blur'' - how far should the glow extend (recommend 1-4).''pulse_speed'' - (0-100). ''min/max brightness'' - floor and ceiling brightness level to target for glows."; - # If set, will show subtitles, if available. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle_enable")] - [Alias('ShowSubtitles','Subtitles')] - [switch] - $Subtitle, - # The selected audio track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("track")] - [int] - $AudioTrack, +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; +} - # The selected subtitle track number. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("subtitle")] - [int] - $SubtitleTrack, +float BlurStyler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; +} - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Scene, +float4 mainImage(VertData v_in) : TARGET +{ + float2 offsets[4]; + offsets[0] = float2(-0.1, 0.125); + offsets[1] = float2(-0.1, -0.125); + offsets[2] = float2(0.1, -0.125); + offsets[3] = float2(0.1, 0.125); - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [string] - $Name, + // convert input for vector math + float4 col = image.Sample(textureSampler, v_in.uv); + float blur_amount = float(blur) /100.0; + float glow_amount = float(glow_percent) * 0.01; + float speed = float(pulse_speed) * 0.01; + float luminance_floor = float(min_brightness) /100.0; + float luminance_ceiling = float(max_brightness) /100.0; - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force, + if (col.a > 0.0) + { + //circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration - # If set, will fit the input to the screen. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $FitToScreen - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' + // simple glow calc + for (int n = 0; n < 4; n++) { + b = BlurStyler(t, 0, c, d, ease); + float4 ncolor = image.Sample(textureSampler, v_in.uv + (blur_amount * b) * offsets[n]); + float intensity = ncolor.r * 0.299 + ncolor.g * 0.587 + ncolor.b * 0.114; + if ((intensity >= luminance_floor) && (intensity <= luminance_ceiling)) + { + ncolor.a = clamp(ncolor.a * glow_amount, 0.0, 1.0); + col += (ncolor * (glow_amount * b)); + } + } + } + return col; +} - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} - } - if (-not $shouldInclude) { continue nextInputParameter } - } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } } - begin { - filter OutputAndFitToScreen { - - if ($FitToScreen -and $_.FitToScreen) { - $_.FitToScreen() - } - $_ - + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - $InputKind = "vlc_source" - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind - } - # (either way, if we were called Get- or with no verb, we're done now). - return + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - $allPaths = @(foreach ($path in $FilePath) { - foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { - $_.Path - } - }) - $playlistObject = @( - $currentIndex = 0 - foreach ($path in $allPaths) { - $currentIndex++ - $selected = $Select -and ( - ($select -is [int] -and $currentIndex -eq $select) -or - ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or - ($select -is [string] -and - ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) - ) - ) - [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} - } - ) - $myParameterData['playlist'] = $playlistObject - - if (-not $Name) { - $Name = $myParameters["Name"] = $FilePathItem.Name + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $InputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($myParameters.Contains('SceneItemEnabled')) { - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} + +} + + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSGradientShader { + +[Alias('Set-OBSGradientShader','Add-OBSGradientShader')] +param( +# Set the start_color of OBSGradientShader +[Alias('start_color')] +[ComponentModel.DefaultBindingProperty('start_color')] +[String] +$StartColor, +# Set the start_step of OBSGradientShader +[Alias('start_step')] +[ComponentModel.DefaultBindingProperty('start_step')] +[Single] +$StartStep, +# Set the middle_color of OBSGradientShader +[Alias('middle_color')] +[ComponentModel.DefaultBindingProperty('middle_color')] +[String] +$MiddleColor, +# Set the middle_step of OBSGradientShader +[Alias('middle_step')] +[ComponentModel.DefaultBindingProperty('middle_step')] +[Single] +$MiddleStep, +# Set the end_color of OBSGradientShader +[Alias('end_color')] +[ComponentModel.DefaultBindingProperty('end_color')] +[String] +$EndColor, +# Set the end_step of OBSGradientShader +[Alias('end_step')] +[ComponentModel.DefaultBindingProperty('end_step')] +[Single] +$EndStep, +# Set the alpha_percent of OBSGradientShader +[Alias('alpha_percent')] +[ComponentModel.DefaultBindingProperty('alpha_percent')] +[Int32] +$AlphaPercent, +# Set the pulse_speed of OBSGradientShader +[Alias('pulse_speed')] +[ComponentModel.DefaultBindingProperty('pulse_speed')] +[Int32] +$PulseSpeed, +# Set the ease of OBSGradientShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the rotate_colors of OBSGradientShader +[Alias('rotate_colors')] +[ComponentModel.DefaultBindingProperty('rotate_colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the Apply_To_Alpha_Layer of OBSGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the horizontal of OBSGradientShader +[ComponentModel.DefaultBindingProperty('horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the vertical of OBSGradientShader +[ComponentModel.DefaultBindingProperty('vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the gradient_center_width_percentage of OBSGradientShader +[Alias('gradient_center_width_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_width_percentage')] +[Int32] +$GradientCenterWidthPercentage, +# Set the gradient_center_height_percentage of OBSGradientShader +[Alias('gradient_center_height_percentage')] +[ComponentModel.DefaultBindingProperty('gradient_center_height_percentage')] +[Int32] +$GradientCenterHeightPercentage, +# Set the notes of OBSGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - } - return - } - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 +process { +$shaderName = 'gradient' +$ShaderNoun = 'OBSGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 start_color = { 0.1, 0.3, 0.1, 1.0 }; +uniform float start_step< + string label = "Start step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.15; +uniform float4 middle_color = { 1.0, 1.0, 1.0, 1.0 }; +uniform float middle_step< + string label = "Middle step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.4; +uniform float4 end_color = { 0.75, 0.75, 0.75, 1.0}; +uniform float end_step< + string label = "End step"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.9; +uniform int alpha_percent< + string label = "Alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform int pulse_speed< + string label = "Pulse speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform bool rotate_colors; +uniform bool Apply_To_Alpha_Layer = true; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform bool horizontal; +uniform bool vertical; +uniform int gradient_center_width_percentage< + string label = "gradient center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int gradient_center_height_percentage< + string label = "gradient center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "gradient center items will change the center location. Pulse Speed greater than 0 will animate. Easing seem to be too fast."; - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } +float EaseInOutCircTimer(float t, float b, float c, float d) { + t /= d / 2; + if (t < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b; + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; +} - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } - } - - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene and optionally fit it to the screen. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name | - OutputAndFitToScreen - } - - } +float BlurStyler(float t, float b, float c, float d, bool ease) +{ + if (ease) return EaseInOutCircTimer(t, 0, c, d); + return t; } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSWaveformSource { - - - [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] - param( - # The width of the browser source. - # If none is provided, this will be the output width of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("width")] - [int] - $Width, - # The width of the browser source. - # If none is provided, this will be the output height of the video settings. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("height")] - [int] - $Height, +struct gradient +{ + float4 color; + float step; +}; - # The audio source for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("audio_source")] - [string] - $AudioSource, - # The display mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("display_mode")] - [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] - [string] - $DisplayMode, +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.14159265f;//acos(-1); + float speed = float(pulse_speed) * 0.01; + float alpha = float(alpha_percent) * 0.01; + + //circular easing variable + float t = sin(elapsed_time * speed) * 2 - 1; + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration - # The render mode for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("line","solid","gradient")] - [string] - $RenderMode, + float2 gradient_center = float2(float(gradient_center_width_percentage) * 0.01,float(gradient_center_height_percentage) * 0.01); + float4 color = image.Sample(textureSampler, v_in.uv); + float luminance = color.a * 0.299 + color.g * 0.587 + color.b * 0.114; + float4 gray = float4(luminance,luminance,luminance, 1); - # The windowing mode for the waveform. - # This is the mathematical function used to determine the current "window" of audio data. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("render_mode")] - [ValidateSet("hann","hamming","blackman","blackman_harris","none")] - [string] - $WindowMode, + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + b = BlurStyler(t, 0, c, d, ease); - # The color used for the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_base")] - [PSObject] - $Color, + const int no_colors = 3; + float4 s_color = start_color; + float4 m_color = middle_color; + float4 e_color = end_color; - # The crest color used for the waveform. - # This will be ignored if the render mode is not "gradient". - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("color_crest")] - [PSObject] - $CrestColor, + if (rotate_colors) + { + // get general time number between 0 and 4 + float tx = (b + 1) * 2; + // 3 steps c1->c2, c2->c3, c3->c1 + //when between 0 - 1 only c1 rises then falls - # The channel mode for the waveform. - # This can be either mono or stereo. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_mode")] - [ValidateSet("mono","stereo")] - [string] - $ChannelMode, + if (tx <= 2.0) + { + s_color = lerp(start_color, middle_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(middle_color, end_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(end_color, start_color, clamp((min(tx, 2.0) * 0.5) * 2, 0.0, 1.0)); + } - # The number of pixels between each channel in stereo mode - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("channel_spacing")] - [int] - $ChannelSpacing, + if ((tx >= 1.0) && (tx <= 3.0)) + { + s_color = lerp(middle_color, end_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(end_color, start_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(start_color, middle_color, clamp(((min(max(1.0, tx), 3.0) - 1) * 0.5) * 2, 0.0, 1.0)); + } - # If set, will use a radial layout for the waveform - # Radial layouts will ignore the desired height of the source and instead create a square. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("radial_layout")] - [switch] - $RadialLayout, + if (tx >= 2.0) + { + s_color = lerp(end_color, start_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(((min(2.0, tx) - 2) * 0.5) * 2, 0.0, 1.0)); + } - # If set, will invert the direction for a radial waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("invert_direction")] - [switch] - $InvertRadialDirection, + if (tx < 0) + { + s_color = lerp(end_color, start_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + m_color = lerp(start_color, middle_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + e_color = lerp(middle_color, end_color, clamp(abs(max(1.0, tx)) * 2, 0.0, 1.0)); + } + } - # If set, will normalize the volume displayed in the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("normalize_volume")] - [switch] - $NoramlizeVolume, + float4 colors[no_colors]; + colors[0] =s_color; + colors[1] = m_color; + colors[2] = e_color; + float step[no_colors]; + step[0] = start_step; + step[1] = middle_step; + step[2] = end_step; - # If set, will automatically declare an FFTSize - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("auto_fft_size")] - [switch] - $AutoFftSize, + float redness = max(min(color.r - color.g, color.r - color.b) / color.r, 0); + float greenness = max(min(color.g - color.r, color.g - color.b) / color.g, 0); + float blueness = max(min(color.b - color.r, color.b - color.g) / color.b, 0); - # If set, will attempt to make audio peaks render faster. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("fast_peaks")] - [switch] - $FastPeak, + float dist = distance(v_in.uv, gradient_center); + if (horizontal && (vertical == false)) + { + dist = distance(v_in.uv.y, gradient_center.y); + } + if (vertical && (horizontal == false)) + { + dist = distance(v_in.uv.x, gradient_center.x); + } - # The width of the waveform bar. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_width")] - [int] - $BarWidth, + float4 col = colors[0]; + for (int i = 1; i < no_colors; ++i) { + col = lerp(col, colors[i], smoothstep(step[i - 1], step[i], dist)); + } + col.a = clamp(alpha, 0.0, 1.0); + if (Apply_To_Alpha_Layer == false) + color.a = alpha; + if (Apply_To_Specific_Color) + { + col.a = alpha; + float4 original_color = image.Sample(textureSampler, v_in.uv); + col.rgb = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? col.rgb : original_color.rgb; + } + // result = float4(redness, greenness,blueness,1); + //color *= float4(col.r, col.g, col.b, clamp(dot(color, luminance)* alpha, 0.0, 1.0)); + //color.rgb += col * alpha; + //color.a += clamp(1.0 - alpha, 0.0,1.0); + ///color.rgb *= (color.rgb * clamp(1.0- alpha, 0.0, 1.0)) + (col.rgb * clamp(alpha, 0.0, 1.0)); + //color = float4(max(color.r, col.r), max(color.g, col.g), max(color.b, col.b), clamp(dot(color, luminance) * alpha, 0.0, 1.0)); + color.rgb = lerp(color.rgb, col.rgb, clamp(alpha, 0.0, 1.0)); - # The gap between waveform bars. - # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("bar_gap")] - [int] - $BarGap, + } + return color; - # The width of waveform bar step. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_width")] - [int] - $StepWidth, + +} - # The gap between waveform bar steps. - # This is only valid when -DisplayMode is 'stepped_bars' - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("step_gap")] - [int] - $StepGap, +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # The low-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_low")] - [int] - $LowCutoff, + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } - # The high-frequency cutoff of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cutoff_high")] - [int] - $HighCutoff, + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # The floor of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("floor")] - [int] - $Floor, + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } + } - # The ceiling of the waveform. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("ceiling")] - [int] - $Ceiling, + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("slope")] - [double] - $Slope, + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_q")] - [Alias('RollOffOctaves')] - [double] - $RollOffOctave, + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("rolloff_rate")] - [double] - $RollOffRate, +} - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("grad_ratio")] - [double] - $GradientRatio, - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("deadzone")] - [double] - $Deadzone, +} - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("temporal_smoothing")] - [ValidateSet("none","exp_moving_avg")] - [string] - $TemporalSmoothing, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSHalftoneShader { - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, +[Alias('Set-OBSHalftoneShader','Add-OBSHalftoneShader')] +param( +# Set the threshold of OBSHalftoneShader +[ComponentModel.DefaultBindingProperty('threshold')] +[Single] +$Threshold, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The name of the input. - # If no name is provided, the last segment of the URI or file path will be the input name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +process { +$shaderName = 'halftone' +$ShaderNoun = 'OBSHalftoneShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.1415926535897932384626433832795 +#define PI180 float(PI / 180.0) +uniform float threshold< + string label = "Threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.6; - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} +float sind(float a) +{ + return sin(a * PI180); +} + +float cosd(float a) +{ + return cos(a * PI180); +} + +float added(float2 sh, float sa, float ca, float2 c, float d) +{ + return 0.5 + 0.25 * cos((sh.x * sa + sh.y * ca + c.x) * d) + 0.25 * cos((sh.x * ca - sh.y * sa + c.y) * d); +} + +float4 mainImage(VertData v_in) : TARGET +{ + // Halftone dot matrix shader + // @author Tomek Augustyn 2010 + + // Ported from my old PixelBender experiment + // https://github.com/og2t/HiSlope/blob/master/src/hislope/pbk/fx/halftone/Halftone.pbk + + float coordX = v_in.uv.x; + float coordY = v_in.uv.y; + float2 dstCoord = float2(coordX, coordY); + float2 rotationCenter = float2(0.5, 0.5); + float2 shift = dstCoord - rotationCenter; + + float dotSize = 3.0; + float angle = 45.0; + + float rasterPattern = added(shift, sind(angle), cosd(angle), rotationCenter, PI / dotSize * 680.0); + float4 srcPixel = image.Sample(textureSampler, dstCoord); + + float avg = 0.2125 * srcPixel.r + 0.7154 * srcPixel.g + 0.0721 * srcPixel.b; + float gray = (rasterPattern * threshold + avg - threshold) / (1.0 - threshold); + + // uncomment to see how the raster pattern looks + // gray = rasterPattern; + + return float4(gray, gray, gray, 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - $inputKind = "phandasm_waveform_source" - filter ToOBSColor { - - if ($_ -is [uint32]) { $_ } - elseif ($_ -is [string]) { - if ($_ -match '^\#[a-f0-9]{3,4}$') { - $_ = $_ -replace '[a-f0-9]','$0$0' - } - - if ($_ -match '^#[a-f0-9]{8}$') { - ( - '0x' + - (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') - ) -as [UInt32] - } - elseif ($_ -match '^#[a-f0-9]{6}$') { - - ( - '0xff' + - (($_ -replace '#').ToCharArray()[-1..-6] -join '') - ) -as [UInt32] - } - } - + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - } - process { - $myParameters = [Ordered]@{} + $PSBoundParameters - - $MyInvocationName = "$($MyInvocation.InvocationName)" - $myVerb, $myNoun = $MyInvocationName -split '-' - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - $IsGet = $myVerb -eq "Get" - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - if ( - $IsGet -or - $NoVerb - ) { - $inputsOfKind = - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { - $_.InputName -like $Name - } else { - $_ - } - } - if ($NonNameParameters -and -not $IsGet) { - $paramCopy = [Ordered]@{} + $PSBoundParameters - if ($paramCopy.Name) { $paramCopy.Remove('Name') } - $inputsOfKind | & $myScriptBlock @paramCopy - } else { - $inputsOfKind - } - return + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName } - - if ((-not $width) -or (-not $height)) { - if (-not $script:CachedOBSVideoSettings) { - $script:CachedOBSVideoSettings = Get-OBSVideoSettings + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - $videoSettings = $script:CachedOBSVideoSettings - $myParameters["Width"] = $width = $videoSettings.outputWidth - $myParameters["Height"] = $height = $videoSettings.outputHeight } - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} +} - - if (-not $Name) { - $Name = $myParameters['Name'] = - if ($AudioSource) { - "$($AudioSource)-Waveform" - } else { - "Waveform" - } + +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSHardBlinkShader { + +[Alias('Set-OBSHardBlinkShader','Add-OBSHardBlinkShader')] +param( +# Set the timeon of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeon')] +[Single] +$Timeon, +# Set the timeoff of OBSHardBlinkShader +[ComponentModel.DefaultBindingProperty('timeoff')] +[Single] +$Timeoff, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'hard_blink' +$ShaderNoun = 'OBSHardBlinkShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// hard_blink shader created by https://github.com/WhazzItToYa +// +// Periodically makes the source image 100% transparent, in configurable intervals. + +uniform float timeon< + string label = "Time On"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; + +uniform float timeoff< + string label = "Time Off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 0.5; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float m = timeon + timeoff; + float t = elapsed_time % m; + if (t < timeon) { + return color; + } else { + return float4(color.r, color.g, color.b, 0.0); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - if ($myParameterData.color_base) { - $myParameterData.color_base = $myParameterData.color_base | ToOBSColor + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($myParameterData.color_crest) { - $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - $addSplat = [Ordered]@{ - sceneName = $myParameters["Scene"] - inputKind = $inputKind - inputSettings = $myParameterData - inputName = $Name - NoResponse = $myParameters["NoResponse"] - } - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - $addSplat.Passthru = $MyParameters["PassThru"] - # If we were called with Add- - if ($MyInvocation.InvocationName -like 'Add-*') { - Add-OBSInput @addSplat # passthru Add-OBSInput - } else { - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - return } - - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If we got back an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # and that error was saying the source already exists, - if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { - # then check if we use the -Force. - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. - $outputAddedResult = $null - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } - # Otherwise, if we had a result - if ($outputAddedResult -and - $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } - } } - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSWindowSource { - - - [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] - param( - # The monitor number. - # This the number of the monitor you would like to capture. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] - [string] - $WindowTitle, - # The number of the capture method. By default, automatic (0). - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("method")] - [int] - $CaptureMethod, +} - # The capture priority. - [Parameter(ValueFromPipelineByPropertyName)] - [ValidateSet('ExactMatch','SameType','SameExecutable')] - [string] - $CapturePriority, - # If set, will capture the cursor. - # This will be set by default. - # If explicitly set to false, the cursor will not be captured. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("cursor")] - [switch] - $CaptureCursor, +} - # If set, will capture the client area. - # This will be set by default. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("client_area")] - [switch] - $ClientArea, + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSHeatWaveSimpleShader { - # If set, will force SDR. - [Parameter(ValueFromPipelineByPropertyName)] - [ComponentModel.DefaultBindingProperty("force_sdr")] - [switch] - $ForceSDR, +[Alias('Set-OBSHeatWaveSimpleShader','Add-OBSHeatWaveSimpleShader')] +param( +# Set the Rate of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Rate')] +[Single] +$Rate, +# Set the Strength of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Strength')] +[Single] +$Strength, +# Set the Distortion of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Distortion')] +[Single] +$Distortion, +# Set the Opacity of OBSHeatWaveSimpleShader +[ComponentModel.DefaultBindingProperty('Opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) - # The name of the scene. - # If no scene name is provided, the current program scene will be used. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('SceneName')] - [string] - $Scene, - # The name of the input. - # If no name is provided, "Display $($Monitor + 1)" will be the input source name. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('InputName')] - [string] - $Name, +process { +$shaderName = 'heat-wave-simple' +$ShaderNoun = 'OBSHeatWaveSimpleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Heat Wave Simple, Version 0.03, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// +// Description: +// Generate a crude pseudo heat wave displacement on an image source. +// +// Based on: https://www.shadertoy.com/view/td3GRn by Dombass +// +// Changelog: +// 0.03 - Added Opacity control +// 0.02 - Added crude Rate, Strength, and Distortion controls +// 0.01 - Initial release - # If set, will check if the source exists in the scene before creating it and removing any existing sources found. - # If not set, you will get an error if a source with the same name exists. - [Parameter(ValueFromPipelineByPropertyName)] - [switch] - $Force - ) - dynamicParam { - $baseCommand = - if (-not $script:AddOBSInput) { - $script:AddOBSInput = - $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') - $script:AddOBSInput - } else { - $script:AddOBSInput - } - $IncludeParameter = @() - $ExcludeParameter = 'inputKind','sceneName','inputName' +uniform float Rate< + string label = "Rate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 5.0; +uniform float Strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = -25.0; + float maximum = 25.0; + float step = 0.01; +> = 1.0; +uniform float Distortion< + string label = "Distortion"; + string widget_type = "slider"; + float minimum = 3.0; + float maximum = 20.0; + float step = 0.01; +> = 10.0; +uniform float Opacity< + string label = "Opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.00; +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float distort = clamp(Distortion, 3.0, 20.0); - $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() - :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { - if ($ExcludeParameter) { - foreach ($exclude in $ExcludeParameter) { - if ($paramName -like $exclude) { continue nextInputParameter} + // Time varying pixel color + float jacked_time = Rate*elapsed_time; + float2 scale = float2(0.5, 0.5); + float str = clamp(Strength, -25.0, 25.0) * 0.01; + + uv += str * sin(scale*jacked_time + length( uv ) * distort); + float4 c = image.Sample( textureSampler, uv); + c.a *= saturate(Opacity*0.01); + return c; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - if ($IncludeParameter) { - $shouldInclude = - foreach ($include in $IncludeParameter) { - if ($paramName -like $include) { $true;break} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - if (-not $shouldInclude) { continue nextInputParameter } + continue nextParameter + } } - - $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( - $baseCommand.Parameters[$paramName].Name, - $baseCommand.Parameters[$paramName].ParameterType, - $baseCommand.Parameters[$paramName].Attributes - )) - } - $DynamicParameters - } - begin { - $InputKind = "window_capture" - - } - process { - # Copy the bound parameters - $myParameters = [Ordered]@{} + $PSBoundParameters - # and determine the name of the invocation - $MyInvocationName = "$($MyInvocation.InvocationName)" - # Split it into verb and noun - $myVerb, $myNoun = $MyInvocationName -split '-' - # and get a copy of ourself that we can call with anonymous recursion. - $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock - # Determine if the verb was get, - $IsGet = $myVerb -eq "Get" - # if no verb was used, - $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' - # and if there were any other parameters then name - $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } - # If it is a get or there was no verb - if ($IsGet -or $NoVerb) { - $inputsOfKind = # Get all inputs of this kind - Get-OBSInput -InputKind $InputKind | - Where-Object { - if ($Name) { # If -Name was provided, - $_.InputName -like $Name # filter by name (as a wildcard). - } else { - $_ # otherwise, return every input. - } - } - - # If there were parameters other than name, - # and we were not explicitly called Get-* - if ($NonNameParameters -and -not $IsGet) { - # remove the name parameter - if ($myParameters.Name) { $myParameters.Remove('Name') } - # and pipe results back to ourself. - $inputsOfKind | & $myScriptBlock @myParameters - } else { - # Otherwise, we're just getting the list of inputs - $inputsOfKind + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } - # (either way, if we were called Get- or with no verb, we're done now). - return } - - if (-not $myParameters["Scene"]) { - $myParameters["Scene"] = Get-OBSCurrentProgramScene | - Select-Object -ExpandProperty currentProgramSceneName + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - if (-not $myParameters["WindowTitle"]) { - while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { - $_ = $_.Parent - } - if ($_.MainWindowTitle) { - $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" - } + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - # Window capture is a bit of a tricky one. - # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. - # and for that, an input needs to exist. - if (-not $myParameters["Name"]) { - if ($myParameters["WindowTitle"]) { - $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] - } - else { - $Name = "WindowCapture" - } + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} - +} - - $myParameterData = [Ordered]@{} - foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - $bindToPropertyName = $null - - foreach ($attribute in $parameter.Attributes) { - if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { - $bindToPropertyName = $attribute.Name - break - } - } +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSHexagonShader { + +[Alias('Set-OBSHexagonShader','Add-OBSHexagonShader')] +param( +# Set the Hex_Color of OBSHexagonShader +[Alias('Hex_Color')] +[ComponentModel.DefaultBindingProperty('Hex_Color')] +[String] +$HexColor, +# Set the Alpha_Percent of OBSHexagonShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Int32] +$AlphaPercent, +# Set the Quantity of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Quantity')] +[Single] +$Quantity, +# Set the Border_Width of OBSHexagonShader +[Alias('Border_Width')] +[ComponentModel.DefaultBindingProperty('Border_Width')] +[Int32] +$BorderWidth, +# Set the Blend of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Blend')] +[Management.Automation.SwitchParameter] +$Blend, +# Set the Equilateral of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Equilateral')] +[Management.Automation.SwitchParameter] +$Equilateral, +# Set the Zoom_Animate of OBSHexagonShader +[Alias('Zoom_Animate')] +[ComponentModel.DefaultBindingProperty('Zoom_Animate')] +[Management.Automation.SwitchParameter] +$ZoomAnimate, +# Set the Speed_Percent of OBSHexagonShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Int32] +$SpeedPercent, +# Set the Glitch of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Distort_X of OBSHexagonShader +[Alias('Distort_X')] +[ComponentModel.DefaultBindingProperty('Distort_X')] +[Single] +$DistortX, +# Set the Distort_Y of OBSHexagonShader +[Alias('Distort_Y')] +[ComponentModel.DefaultBindingProperty('Distort_Y')] +[Single] +$DistortY, +# Set the Offset_X of OBSHexagonShader +[Alias('Offset_X')] +[ComponentModel.DefaultBindingProperty('Offset_X')] +[Single] +$OffsetX, +# Set the Offset_Y of OBSHexagonShader +[Alias('Offset_Y')] +[ComponentModel.DefaultBindingProperty('Offset_Y')] +[Single] +$OffsetY, +# Set the notes of OBSHexagonShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'hexagon' +$ShaderNoun = 'OBSHexagonShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Hexagon shader by Charles Fettinger for obs-shaderfilter plugin 4/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 Hex_Color; +uniform int Alpha_Percent< + string label = "Alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform float Quantity< + string label = "Quantity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 25; +uniform int Border_Width< + string label = "Border Width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 115; + int step = 1; +> = 15; // <- -15 to 85, -15 off top +uniform bool Blend; +uniform bool Equilateral; +uniform bool Zoom_Animate; +uniform int Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 100; +uniform bool Glitch; +uniform float Distort_X< + string label = "Distort X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Distort_Y< + string label = "Distort Y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float Offset_X< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Offset_Y< + string label = "Offset X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform string notes< + string widget_type = "info"; +>= "Tiles:equilateral: around 12.33,nonequilateral: square rootable number. Distort of 1 is normal."; + +float mod(float x, float y) +{ + return x - y * floor(x/y); +} - if (-not $bindToPropertyName) { continue } - if ($myParameters.Contains($parameter.Name)) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] - if ($myParameters[$parameter.Name] -is [switch]) { - $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] - } - } - } +float2 mod2(float2 x, float2 y) +{ + return x - y * floor(x/y); +} - if ($null -ne $CaptureMethod) { - $myParameterData["method"] = $CaptureMethod - } - if ($CapturePriority -eq 'ExactMatch') { - $myParameterData["priority"] = 1 - } - elseif ($CapturePriority -eq 'SameType') { - $myParameterData["priority"] = 0 - } - elseif ($CapturePriority -eq 'SameExecutable') { - $myParameterData["priority"] = 2 - } +// 0 on edges, 1 in non_edge +float hex(float2 p) { + float xyratio = 1; + if (Equilateral) + xyratio = uv_size.x /uv_size.y; - $addSplat = @{ - sceneName = $myParameters["Scene"] - inputName = $myParameters["Name"] - inputKind = "window_capture" - inputSettings = $myParameterData - NoResponse = $myParameters["NoResponse"] - } + // calc p + p.x = mul(p.x,xyratio); + p.y += mod(floor(p.x) , 2.0)*0.5; + p = abs((mod2(p , float2(1.0, 1.0)) - 0.5)); + return abs(max(p.x*1.5 + p.y, p.y*2.0) -1); +} - # If -SceneItemEnabled was passed, - if ($myParameters.Contains('SceneItemEnabled')) { - # propagate it to Add-OBSInput. - $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv * uv_scale + uv_offset); + float alpha = float(Alpha_Percent) * 0.01; + float quantity = sqrt(clamp(Quantity, 0.0, 100.0)); + float border_width = clamp(float(Border_Width - 15), -15, 100) * 0.01; + float speed = float(Speed_Percent) * 0.01; + float time = (1 + sin(elapsed_time * speed))*0.5; + if (Zoom_Animate) + quantity *= time; - # Add the input. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window - foreach ($windowInfo in $possibleWindows) { - if (-not $WindowTitle) { continue } - if ( - ($windowInfo.itemName -eq $WindowTitle) -or - ($windowInfo.ItemValue -eq $WindowTitle) -or - ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or - ($windowInfo.ItemValue -like "*$WindowTitle*") -or - ($windowInfo.ItemName -like "*$WindowTitle*") - ) { - $myParameterData["window"] = $windowInfo.itemValue - break - } - } + // create a (pos)ition reference, hex radius and smoothstep out the non_edge + float2 pos = float2(v_in.uv.x * max(0,Distort_X), (1 - v_in.uv.y) * max(0,Distort_Y)) * uv_scale + uv_offset + float2(Offset_X, Offset_Y); + if (Glitch) + quantity *= lerp(pos.x, pos.y, rand_f); + float2 p = (pos * quantity); // number of hexes to be created + float r = (1.0 -0.7)*0.5; // cell default radius + float non_edge = smoothstep(0.0, r + border_width, hex(p)); // approach border become edge - # If -PassThru was passed - if ($MyParameters["PassThru"]) { - # pass it down to each command - # Otherwise, remove SceneItemEnabled, InputKind, and SceneName - $addSplat.PassThru = $true - $addSplat.Remove('SceneItemEnabled') - $addSplat.Remove('inputKind') - $addSplat.Remove('sceneName') - # and passthru Set-OBSInputSettings. - Set-OBSInputSettings @addSplat - - return - } + // make the border colorable - non_edge is scaled + float4 c = float4(non_edge, non_edge,non_edge,1.0) ; + if (non_edge < 1) + { + c = Hex_Color; + c.a = alpha; + if (Blend) + c = lerp(rgba, c, 1 - non_edge); + return lerp(rgba,c,alpha); + } + return lerp(rgba, c * rgba, alpha); +} - if ($Force) { # If we do, remove the input - Remove-OBSInput -InputName $addSplat.inputName - # and re-add our result. - $outputAddedResult = Add-OBSInput @addSplat *>&1 - # If the output was still an error - if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { - # use $psCmdlet.WriteError so that it shows the error correctly. - $psCmdlet.WriteError($outputAddedResult) +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - # Otherwise, if we had a result - elseif ($outputAddedResult) { - # get the input from the scene. - Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $name + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - # Otherwise, get the input from the scene, - $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | - Where-Object SourceName -eq $myParameters["Name"] - # update the input settings - $sceneItem.Input.Settings = $addSplat.inputSettings - $sceneItem # and return the scene item. + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - } -} - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSInput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateInput')] -[Alias('obs.powershell.websocket.CreateInput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -49967,213 +42741,316 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSProfile { - +function Get-OBSHslHsvSaturationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateProfile')] -[Alias('obs.powershell.websocket.CreateProfile')] +[Alias('Set-OBSHslHsvSaturationShader','Add-OBSHslHsvSaturationShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the hslSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslSaturationFactor')] +[Single] +$HslSaturationFactor, +# Set the hslGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hslGamma')] +[Single] +$HslGamma, +# Set the hsvSaturationFactor of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvSaturationFactor')] +[Single] +$HsvSaturationFactor, +# Set the hsvGamma of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('hsvGamma')] +[Single] +$HsvGamma, +# Set the adjustmentOrder of OBSHslHsvSaturationShader +[ComponentModel.DefaultBindingProperty('adjustmentOrder')] +[Int32] +$AdjustmentOrder, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hsl_hsv_saturation' +$ShaderNoun = 'OBSHslHsvSaturationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Adjusted Saturation Shader for obs-shaderfilter using HLSL conventions - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float hslSaturationFactor< + string label = "HSL Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float hslGamma< + string label = "HSL Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float hsvSaturationFactor< + string label = "HSV Sat Gain"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.01; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float hsvGamma< + string label = "HSV Sat Gamma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 1.0; + +uniform int adjustmentOrder< + string label = "Order"; + string widget_type = "select"; + int option_0_value = 1; + string option_0_label = "Parallel adjustment (both HSL and HSV operate on the original image and then blend)"; + int option_1_value = 2; + string option_1_label = "HSL first, then HSV"; + int option_2_value = 3; + string option_2_label = "HSV first, then HSL"; +> = 1; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +// HSV conversion +float3 rgb2hsv(float3 c) { + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } +float3 hsv2rgb(float3 c) { + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} -} +// HSL conversion - -#.ExternalHelp obs-powershell-Help.xml -function Add-OBSScene { +float3 rgb2hsl(float3 c) { + float maxVal = max(c.r, max(c.g, c.b)); + float minVal = min(c.r, min(c.g, c.b)); + float delta = maxVal - minVal; + float h = 0.0; + float s = 0.0; + float l = (maxVal + minVal) / 2.0; + if(delta != 0) { + if(l < 0.5) s = delta / (maxVal + minVal); + else s = delta / (2.0 - maxVal - minVal); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateScene')] -[Alias('obs.powershell.websocket.CreateScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + if(c.r == maxVal) h = (c.g - c.b) / delta + (c.g < c.b ? 6.0 : 0.0); + else if(c.g == maxVal) h = (c.b - c.r) / delta + 2.0; + else h = (c.r - c.g) / delta + 4.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + h /= 6.0; + } + return float3(h, s, l); +} -process { +float hue2rgb(float p, float q, float t) { + if(t < 0.0) t += 1.0; + if(t > 1.0) t -= 1.0; + if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if(t < 1.0/2.0) return q; + if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; +} +float3 hsl2rgb(float3 c) { + float r, g, b; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if(c.y == 0.0) { + r = g = b = c.z; + } else { + float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y; + float p = 2.0 * c.z - q; + r = hue2rgb(p, q, c.x + 1.0/3.0); + g = hue2rgb(p, q, c.x); + b = hue2rgb(p, q, c.x - 1.0/3.0); + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + return float3(r, g, b); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float3 adjustColorWithOrder(float3 originalColor) { + if (adjustmentOrder == 1) { + // Parallel adjustment (both HSL and HSV operate on the original image and then blend) + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 hslAdjustedColor = hsl2rgb(hslAdjusted); + + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 hsvAdjustedColor = hsv2rgb(hsvAdjusted); + + float3 finalColor = (hslAdjustedColor + hsvAdjustedColor) * 0.5; + return finalColor; + } + else if (adjustmentOrder == 2) { + // HSL first, then HSV + float3 hslAdjusted = rgb2hsl(originalColor); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + float3 afterHSL = hsl2rgb(hslAdjusted); + float3 hsvAdjusted = rgb2hsv(afterHSL); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + return hsv2rgb(hsvAdjusted); + } + else if (adjustmentOrder == 3) { + // HSV first, then HSL + float3 hsvAdjusted = rgb2hsv(originalColor); + hsvAdjusted.y = pow(hsvAdjusted.y, (1/hsvGamma)); + hsvAdjusted.y *= hsvSaturationFactor; + float3 afterHSV = hsv2rgb(hsvAdjusted); + float3 hslAdjusted = rgb2hsl(afterHSV); + hslAdjusted.y = pow(hslAdjusted.y, (1/hslGamma)); + hslAdjusted.y *= hslSaturationFactor; + return hsl2rgb(hslAdjusted); + } + return originalColor; // Default to original color in case of unexpected values +} + +// Final composite + +float4 mainImage(VertData v_in) : TARGET +{ + float3 originalColor = image.Sample(textureSampler, v_in.uv).rgb; + float3 adjustedColor = adjustColorWithOrder(originalColor); + return float4(adjustedColor, 1.0); // preserving the original alpha +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50182,105 +43059,235 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneCollection { - +function Get-OBSHueRotatonShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneCollection')] -[Alias('obs.powershell.websocket.CreateSceneCollection')] +[Alias('Set-OBSHueRotatonShader','Add-OBSHueRotatonShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Speed of OBSHueRotatonShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Hue_Override of OBSHueRotatonShader +[Alias('Hue_Override')] +[ComponentModel.DefaultBindingProperty('Hue_Override')] +[Management.Automation.SwitchParameter] +$HueOverride, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'hue-rotaton' +$ShaderNoun = 'OBSHueRotatonShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Hue Rotation shader, version 1.0 for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: GNU General Public License, version 2 +// +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Description: +// Rotates hue of input at a user configurable speed. Negative speed values reverse rotation. A hue +// override option is provided to force a specific rotating hue instead of the original image''s hue. +// +// Changelog: +// 1.0 - Initial release +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 10.00; +uniform bool Hue_Override = false; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); +} + +#define Epsilon 1e-10 + +float3 RGBtoHCV(in float3 RGB) +{ + // Based on work by Sam Hocevar and Emil Persson + float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0); + float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx); + float C = Q.x - min(Q.w, Q.y); + float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z); + return float3(H, C, Q.x); +} + +float3 HSVtoRGB(in float3 HSV) +{ + float3 RGB = HUEtoRGB(HSV.x); + return ((RGB - 1) * HSV.y + 1) * HSV.z; +} + +float3 RGBtoHSV(in float3 RGB) +{ + float3 HCV = RGBtoHCV(RGB); + float S = HCV.y / (HCV.z + Epsilon); + return float3(HCV.x, S, HCV.z); +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float4 col_in = image.Sample(textureSampler, uv); + float3 col_out; + float3 HSV = RGBtoHSV(col_in.rgb); + + if(Hue_Override) + HSV.x = elapsed_time * Speed * 0.01; + else + HSV.x += elapsed_time * Speed * 0.01; + + // Normalize Hue + HSV.x = frac(HSV.x); + + col_out = HSVtoRGB(HSV); + return float4(col_out, col_in.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50289,127 +43296,192 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSceneItem { - +function Get-OBSIntensityScopeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSceneItem')] -[Alias('obs.powershell.websocket.CreateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -[Alias('Add-OBSSceneSource')] +[Alias('Set-OBSIntensityScopeShader','Add-OBSIntensityScopeShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - +# Set the gain of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('gain')] +[Single] +$Gain, +# Set the blend of OBSIntensityScopeShader +[ComponentModel.DefaultBindingProperty('blend')] +[Single] +$Blend, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'intensity-scope' +$ShaderNoun = 'OBSIntensityScopeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Robin Green, Dec 2016 +// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// https://www.shadertoy.com/view/XtcSRs adopted for OBS by Exeldro +uniform float gain< + string label = "Gain"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 1.00; + float step = 0.01; +> = 0.3; +uniform float blend< + string label = "Blend"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.00; + float step = 0.01; +> = 0.6; +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv.y = 1.0 - uv.y; + + // calculate the intensity bucket for this pixel based on column height (padded at the top) + const float max_value = 270.0; + const float buckets = 512.0; + float bucket_min = log( max_value * floor(uv.y * buckets) / buckets ); + float bucket_max = log( max_value * floor((uv.y * buckets) + 1.0) / buckets ); + + // count the count the r,g,b and luma in this column that match the bucket + float4 count = float4(0.0, 0.0, 0.0, 0.0); + for( int i=0; i < 512; ++i ) { + float j = float(i) / buckets; + float4 pixel = image.Sample(textureSampler, float2(uv.x, j )) * 256.0; + + // calculate the Rec.709 luma for this pixel + pixel.a = pixel.r * 0.2126 + pixel.g * 0.7152 + pixel.b * 0.0722; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 logpixel = log(pixel); + if( logpixel.r >= bucket_min && logpixel.r < bucket_max) count.r += 1.0; + if( logpixel.g >= bucket_min && logpixel.g < bucket_max) count.g += 1.0; + if( logpixel.b >= bucket_min && logpixel.b < bucket_max) count.b += 1.0; + if( logpixel.a >= bucket_min && logpixel.a < bucket_max) count.a += 1.0; + } + + // sum luma into RGB, tweak log intensity for readability + count.rgb = log(count.rgb * (1.0-blend) + count.aaa * blend) * gain; + + // output + return count; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50418,125 +43490,266 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Add-OBSSourceFilter { - +function Get-OBSInvertLumaShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CreateSourceFilter')] -[Alias('obs.powershell.websocket.CreateSourceFilter')] +[Alias('Set-OBSInvertLumaShader','Add-OBSInvertLumaShader')] param( - +# Set the Invert_Color of OBSInvertLumaShader +[Alias('Invert_Color')] +[ComponentModel.DefaultBindingProperty('Invert_Color')] +[Management.Automation.SwitchParameter] +$InvertColor, +# Set the Invert_Luma of OBSInvertLumaShader +[Alias('Invert_Luma')] +[ComponentModel.DefaultBindingProperty('Invert_Luma')] +[Management.Automation.SwitchParameter] +$InvertLuma, +# Set the Gamma_Correction of OBSInvertLumaShader +[Alias('Gamma_Correction')] +[ComponentModel.DefaultBindingProperty('Gamma_Correction')] +[Management.Automation.SwitchParameter] +$GammaCorrection, +# Set the Test_Ramp of OBSInvertLumaShader +[Alias('Test_Ramp')] +[ComponentModel.DefaultBindingProperty('Test_Ramp')] +[Management.Automation.SwitchParameter] +$TestRamp, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'invert-luma' +$ShaderNoun = 'OBSInvertLumaShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Invert shader 1.0 - for OBS Shaderfilter +// Copyright 2021 by SkeletonBow +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv +// Performs RGB color inversion or YUV luma inversion with optional sRGB gamma handling - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool Invert_Color = false; +uniform bool Invert_Luma = true; +uniform bool Gamma_Correction = true; +uniform bool Test_Ramp = false; + +float3 encodeSRGB(float3 linearRGB) +{ + float3 a = float3(12.92,12.92,12.92) * linearRGB; + float3 b = float3(1.055,1.055,1.055) * pow(linearRGB, float3(1.0 / 2.4,1.0 / 2.4,1.0 / 2.4)) - float3(0.055,0.055,0.055); + float3 c = step(float3(0.0031308,0.0031308,0.0031308), linearRGB); + return float3(lerp(a, b, c)); +} + +float3 decodeSRGB(float3 screenRGB) +{ + float3 a = screenRGB / float3(12.92,12.92,12.92); + float3 b = pow((screenRGB + float3(0.055,0.055,0.055)) / float3(1.055,1.055,1.055), float3(2.4,2.4,2.4)); + float3 c = step(float3(0.04045,0.04045,0.04045), screenRGB); + return float3(lerp(a, b, c)); +} + +float3 HUEtoRGB(in float H) +{ + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return float3(clamp(float3(R,G,B), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0))); +} + +float3 RGBtoYUV(float3 color) +{ + // YUV matriz (BT709 luma coefficients) +#ifdef OPENGL + float3x3 toYUV = float3x3( + float3(0.2126, -0.09991, 0.615), + float3(0.7152, -0.33609, -0.55861), + float3(0.0722, 0.436, -0.05639)); +#else + float3x3 toYUV = { + { 0.2126, -0.09991, 0.615 }, + { 0.7152, -0.33609, -0.55861 }, + { 0.0722, 0.436, -0.05639 }, + }; +#endif + return mul(color, toYUV); +} + +float3 YUVtoRGB(float3 color) +{ + // YUV matriz (BT709) +#ifdef OPENGL + float3x3 fromYUV = float3x3( + float3(1.000, 1.000, 1.000), + float3(0.0, -0.21482, 2.12798), + float3(1.28033, -0.38059, 0.0)); +#else + float3x3 fromYUV = { + { 1.000, 1.000, 1.000 }, + { 0.0, -0.21482, 2.12798 }, + { 1.28033, -0.38059, 0.0 }, + }; +#endif + return mul(color, fromYUV); +} + +float3 generate_ramps(float3 color, float2 uv) +{ + float3 ramp = float3(0.0, 0.0, 0.0); + if(uv.y < 0.2) + ramp.r = uv.x; // Red ramp + else if(uv.y < 0.4) + ramp.g = uv.x; // Green ramp + else if(uv.y < 0.6) + ramp.b = uv.x; // Blue ramp + else if(uv.y < 0.8) + ramp = float3(uv.x, uv.x, uv.x); // Grey ramp + else + ramp = HUEtoRGB(uv.x); // Hue rainbow + + return ramp; +} + +float4 mainImage( VertData v_in ) : TARGET +{ + float2 uv = v_in.uv; + float4 obstex = image.Sample( textureSampler, uv ); + float3 color = obstex.rgb; + // Apply sRGB gamma transfer encode + if(Gamma_Correction) color = encodeSRGB( color ); + // Override display with test patterns to visually see what is happening + if( Test_Ramp ) color = generate_ramps( obstex.rgb, uv ); + // RGB color invert + if( Invert_Color ) { + color = float3(1.0, 1.0, 1.0) - color; + } + // YUV luma invert + if( Invert_Luma ) { + float3 yuv = RGBtoYUV( color ); + yuv.x = 1.0 - yuv.x; + color = YUVtoRGB(yuv); + } + // Apply sRGB gamma transfer decode + if(Gamma_Correction) color = decodeSRGB( color ); + return float4(color, obstex.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50545,230 +43758,240 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Copy-OBSSceneItem { - +function Get-OBSLuminance2Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'DuplicateSceneItem')] -[Alias('obs.powershell.websocket.DuplicateSceneItem')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminance2Shader','Add-OBSLuminance2Shader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneName')] -[string] -$DestinationSceneName, - +# Set the color of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the lumaMax of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMax')] +[Single] +$LumaMax, +# Set the lumaMin of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMaxSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMaxSmooth')] +[Single] +$LumaMaxSmooth, +# Set the lumaMinSmooth of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the invertImageColor of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminance2Shader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('destinationSceneUuid')] -[string] -$DestinationSceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'luminance2' +$ShaderNoun = 'OBSLuminance2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform float4 color; +uniform float lumaMax< + string label = "Luma Max"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.05; +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMaxSmooth< + string label = "Luma Max Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.10; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; +uniform string notes< + string widget_type = "info"; +> = "''luma max'' - anything above will be transparent. ''luma min'' - anything below will be transparent. ''luma(min or max)Smooth - make the transparency fade in or out by a distance. ''invert color'' - inverts the color of the screen. ''invert alpha channel'' - flips all settings on thier head, which is excellent for testing."; +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); + } + float luminance = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + //intensity = min(max(intensity,minIntensity),maxIntensity); + float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); + float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } + float amask = clo * chi; + + if (invertAlphaChannel) + { + amask = 1.0 - amask; + } + rgba *= color; + rgba.a = clamp(amask, 0.0, 1.0); - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + } + return rgba; +} - if ($PassThru) { - [PSCustomObject]$requestPayload +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentPreviewScene { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.GetCurrentPreviewScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50777,101 +44000,298 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentProgramScene { - +function Get-OBSLuminanceAlphaShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentProgramScene')] -[Alias('obs.powershell.websocket.GetCurrentProgramScene')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminanceAlphaShader','Add-OBSLuminanceAlphaShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSLuminanceAlphaShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSLuminanceAlphaShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSLuminanceAlphaShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSLuminanceAlphaShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSLuminanceAlphaShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSLuminanceAlphaShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the color_matrix of OBSLuminanceAlphaShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the mul_val of OBSLuminanceAlphaShader +[Alias('mul_val')] +[ComponentModel.DefaultBindingProperty('mul_val')] +[Single] +$MulVal, +# Set the add_val of OBSLuminanceAlphaShader +[Alias('add_val')] +[ComponentModel.DefaultBindingProperty('add_val')] +[Single] +$AddVal, +# Set the level of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('level')] +[Single] +$Level, +# Set the invertAlphaChannel of OBSLuminanceAlphaShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'luminance_alpha' +$ShaderNoun = 'OBSLuminanceAlphaShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Luminance Alpha Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float4x4 color_matrix; +uniform float4 color; +uniform float mul_val< + string label = "Mulitply"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float add_val< + string label = "Add"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 0.0; +uniform float level< + string label = "Level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> =1.0; +uniform bool invertAlphaChannel; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; +}; + +VertDataOut mainTransform(VertDataIn v_in) +{ + VertDataOut vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0 ), ViewProj); + vert_out.uv = v_in.uv * mul_val + add_val; + vert_out.uv2 = v_in.uv ; + return vert_out; +} + +/*float3 GetLuminance(float4 rgba) +{ + float red = rbga.r; + float green = rgba.g; + float blue = rgba.b; + return (.299 * red) + (.587 * green) + (.114 * blue); +}*/ + +float4 PSAlphaMaskRGBA(VertDataOut v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv) ; + if (rgba.a > 0.0) + { + + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + if (invertAlphaChannel) + { + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + + } + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = PSAlphaMaskRGBA(v_in); + } +} + + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50880,101 +44300,205 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransition { - +function Get-OBSLuminanceShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransition')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSLuminanceShader','Add-OBSLuminanceShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the color of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('color')] +[String] +$Color, +# Set the level of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('level')] +[Single] +$Level, +# Set the invertImageColor of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertImageColor')] +[Management.Automation.SwitchParameter] +$InvertImageColor, +# Set the invertAlphaChannel of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('invertAlphaChannel')] +[Management.Automation.SwitchParameter] +$InvertAlphaChannel, +# Set the notes of OBSLuminanceShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Luminance' +$ShaderNoun = 'OBSLuminanceShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 22, 2022 +uniform float4 color; +uniform float level< + string label = "Level"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform bool invertImageColor; +uniform bool invertAlphaChannel; +uniform string notes< + string widget_type = "info"; +> = "''color'' - the color to add to the original image. Multiplies the color against the original color giving it a tint. White represents no tint. ''invertImageColor'' - - inverts the color of the screen, great for testing and fine tuning. ''level'' - transparency amount modifier where 1.0 = base luminance (recommend 0.00 - 10.00). ''invertAlphaChannel'' - flip what is transparent from darks (default) to lights"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 InvertColor(float4 rgba_in) +{ + rgba_in.r = 1.0 - rgba_in.r; + rgba_in.g = 1.0 - rgba_in.g; + rgba_in.b = 1.0 - rgba_in.b; + rgba_in.a = 1.0 - rgba_in.a; + return rgba_in; +} + +float4 mainImage(VertData v_in) : TARGET +{ + + float4 rgba = image.Sample(textureSampler, v_in.uv); + if (rgba.a > 0.0) + { + + if (invertImageColor) + { + rgba = InvertColor(rgba); + } + float intensity = rgba.r * color.r * 0.299 + rgba.g * color.g * 0.587 + rgba.b * color.b * 0.114; + + //intensity = min(max(intensity,minIntensity),maxIntensity); + + + if (invertAlphaChannel) + { + intensity = 1.0 - intensity; + } + rgba *= color; + rgba.a = clamp((intensity * level), 0.0, 1.0); + + } + return rgba; +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -50983,420 +44507,439 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSCurrentSceneTransitionCursor { - +function Get-OBSMatrixShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetCurrentSceneTransitionCursor')] -[Alias('obs.powershell.websocket.GetCurrentSceneTransitionCursor')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMatrixShader','Add-OBSMatrixShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSMatrixShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSMatrixShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSMatrixShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_size of OBSMatrixShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the uv_pixel_interval of OBSMatrixShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSMatrixShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the rand_instance_f of OBSMatrixShader +[Alias('rand_instance_f')] +[ComponentModel.DefaultBindingProperty('rand_instance_f')] +[Single] +$RandInstanceF, +# Set the rand_activation_f of OBSMatrixShader +[Alias('rand_activation_f')] +[ComponentModel.DefaultBindingProperty('rand_activation_f')] +[Single] +$RandActivationF, +# Set the loops of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('loops')] +[Int32] +$Loops, +# Set the local_time of OBSMatrixShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the mouse of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('mouse')] +[Single[]] +$Mouse, +# Set the Invert_Direction of OBSMatrixShader +[Alias('Invert_Direction')] +[ComponentModel.DefaultBindingProperty('Invert_Direction')] +[Management.Automation.SwitchParameter] +$InvertDirection, +# Set the lumaMin of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMin')] +[Single] +$LumaMin, +# Set the lumaMinSmooth of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('lumaMinSmooth')] +[Single] +$LumaMinSmooth, +# Set the Ratio of OBSMatrixShader +[ComponentModel.DefaultBindingProperty('Ratio')] +[Single] +$Ratio, +# Set the Alpha_Percentage of OBSMatrixShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Alpha_Layer of OBSMatrixShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'matrix' +$ShaderNoun = 'OBSMatrixShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Matrix effect by Charles Fettinger for obs-shaderfilter plugin 7/2020 v.2 +// https://github.com/Oncorporation/obs-shaderfilter +// https://www.shadertoy.com/view/XljBW3 The cat is a glitch (Matrix) - coverted from and updated +#define vec2 float2 +#define vec3 float3 +#define vec4 float4 +#define ivec2 int2 +#define ivec3 int3 +#define ivec4 int4 +#define mat2 float2x2 +#define mat3 float3x3 +#define mat4 float4x4 +#define fract frac +#define mix lerp +#define iTime float - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float4x4 ViewProj; +uniform texture2d image; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_size; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float rand_instance_f; +uniform float rand_activation_f; +uniform int loops; +uniform float local_time; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float2 mouse< + string label = "Virtual Mouse Coordinates"; + string widget_type = "slider"; + float2 minimum = {0, 0}; + float2 maximum = {100., 100.}; + float2 scale = {.01, .01}; + float2 step = {.01, .01}; +> = {0., 0.}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +int2 iMouse() { + return int2(mouse.x * uv_size.x, mouse.y * uv_size.y); } +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroup { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupList')] -[Alias('obs.powershell.websocket.GetGroupList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - +/* ps start -process { +*/ +uniform bool Invert_Direction< + string label = "Invert Direction"; +> = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float lumaMin< + string label = "Luma Min"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float lumaMinSmooth< + string label = "Luma Min Smooth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.01; +uniform float Ratio< + string label = "Ratio"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 4.0; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100; // +uniform bool Apply_To_Alpha_Layer = true; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +#define PI2 6.28318530718 +#define PI 3.1416 - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +float vorocloud(float2 p){ + float f = 0.0; + float flow = 1.0; + float time = elapsed_time; + if(Invert_Direction){ + flow *= -1; + } + /* + //periodically stop + if (loops % 16 >= 8.0) + { + time = local_time - elapsed_time; + } + */ + + float r = clamp(Ratio,-50,50); + float2 pp = cos(float2(p.x * 14.0, (16.0 * p.y + cos(floor(p.x * 30.0)) + flow * time * PI2)) ); + p = cos(p * 12.1 + pp * r + sin(time/PI)*(r/PI) + 0.5 * cos(pp.x * r + sin(time/PI)*(r/PI))); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - + float2 pts[4]; + + pts[0] = float2(0.5, 0.6); + pts[1] = float2(-0.4, 0.4); + pts[2] = float2(0.2, -0.7); + pts[3] = float2(-0.3, -0.4); + + float d = 5.0; + + for(int i = 0; i < 4; i++){ + pts[i].x += 0.03 * cos(float(i)) + p.x; + pts[i].y += 0.03 * sin(float(i)) + p.y; + d = min(d, distance(pts[i], pp)); + } + + f = 2.0 * pow(1.0 - 0.3 * d, 13.0); + + f = min(f, 1.0); + + return f; } +vec4 scene(float2 UV){ + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSGroupSceneItem { - + float x = UV.x; + float y = UV.y; + + float2 p = float2(x, y) - 0.5; + + vec4 col = vec4(0.0,0.0,0.0,0.0); + col.g += 0.02; + + float v = vorocloud(p); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.6 * v; + col.b += 0.5 * pow(v, 5.0); + + + v = vorocloud(p * 2.0); + v = 0.2 * floor(v * 5.0); + + col.r += 0.1 * v; + col.g += 0.2 * v; + col.b += 0.01 * pow(v, 5.0); + + col.a = 1.0; + float luma = dot(col.rgb,float3(0.299,0.587,0.114)); + float luma_min = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luma); + col.a = clamp(luma_min,0.0,1.0); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetGroupSceneItemList')] -[Alias('obs.powershell.websocket.GetGroupSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + float4 original_color = image.Sample(textureSampler, UV); + + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(original_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + if (Apply_To_Alpha_Layer == false) + original_color.a = alpha; + + col.rgb = lerp(original_color.rgb, col.rgb, alpha); //apply alpha slider + col = lerp(original_color, col, col.a); //remove black background color + } + else + { + col.a = original_color.a; + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + return col; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / uv_size; + fragColor = scene(uv); +} +/*ps end*/ -process { +struct VertFragData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertFragData VSDefault(VertFragData vtx) { + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 PSDefault(VertFragData vtx) : TARGET { + float4 col = float4(1., 1., 1., 1.); + mainImage(col, vtx.uv * uv_size); + return col; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +technique Draw +{ + pass + { + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); + } +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSHotkey { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetHotkeyList')] -[Alias('obs.powershell.websocket.GetHotkeyList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51405,219 +44948,155 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInput { - +function Get-OBSMotionBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputList')] -[Alias('obs.powershell.websocket.GetInputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMotionBlurShader','Add-OBSMotionBlurShader')] param( - +# Set the previous_output of OBSMotionBlurShader +[Alias('previous_output')] +[ComponentModel.DefaultBindingProperty('previous_output')] +[String] +$PreviousOutput, +# Set the strength of OBSMotionBlurShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'motion_blur' +$ShaderNoun = 'OBSMotionBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d previous_output; +uniform float strength< + string label = "strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + return lerp(image.Sample(textureSampler, v_in.uv), previous_output.Sample(textureSampler, v_in.uv), 1.0 - pow(2, -7 * strength)); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioBalance { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioBalance')] -[Alias('obs.powershell.websocket.GetInputAudioBalance')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51626,111 +45105,147 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioMonitorType { - +function Get-OBSMultiplyShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.GetInputAudioMonitorType')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSMultiplyShader','Add-OBSMultiplyShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the other_image of OBSMultiplyShader +[Alias('other_image')] +[ComponentModel.DefaultBindingProperty('other_image')] +[String] +$OtherImage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'multiply' +$ShaderNoun = 'OBSMultiplyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d other_image; +float4 mainImage(VertData v_in) : TARGET +{ + float4 other = other_image.Sample(textureSampler, v_in.uv); + float4 base = image.Sample(textureSampler, v_in.uv); + return base * other; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -51739,332 +45254,511 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioSyncOffset { - +function Get-OBSNightSkyShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.GetInputAudioSyncOffset')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSNightSkyShader','Add-OBSNightSkyShader')] param( - +# Set the speed of OBSNightSkyShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the Include_Clouds of OBSNightSkyShader +[Alias('Include_Clouds')] +[ComponentModel.DefaultBindingProperty('Include_Clouds')] +[Management.Automation.SwitchParameter] +$IncludeClouds, +# Set the Include_Moon of OBSNightSkyShader +[Alias('Include_Moon')] +[ComponentModel.DefaultBindingProperty('Include_Moon')] +[Management.Automation.SwitchParameter] +$IncludeMoon, +# Set the center_width_percentage of OBSNightSkyShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSNightSkyShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# Set the Alpha_Percentage of OBSNightSkyShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Image of OBSNightSkyShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSNightSkyShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the number_stars of OBSNightSkyShader +[Alias('number_stars')] +[ComponentModel.DefaultBindingProperty('number_stars')] +[Int32] +$NumberStars, +# Set the SKY_COLOR of OBSNightSkyShader +[Alias('SKY_COLOR')] +[ComponentModel.DefaultBindingProperty('SKY_COLOR')] +[String] +$SKYCOLOR, +# Set the STAR_COLOR of OBSNightSkyShader +[Alias('STAR_COLOR')] +[ComponentModel.DefaultBindingProperty('STAR_COLOR')] +[String] +$STARCOLOR, +# Set the LIGHT_SKY of OBSNightSkyShader +[Alias('LIGHT_SKY')] +[ComponentModel.DefaultBindingProperty('LIGHT_SKY')] +[String] +$LIGHTSKY, +# Set the SKY_LIGHTNESS of OBSNightSkyShader +[Alias('SKY_LIGHTNESS')] +[ComponentModel.DefaultBindingProperty('SKY_LIGHTNESS')] +[Single] +$SKYLIGHTNESS, +# Set the MOON_COLOR of OBSNightSkyShader +[Alias('MOON_COLOR')] +[ComponentModel.DefaultBindingProperty('MOON_COLOR')] +[String] +$MOONCOLOR, +# Set the moon_size of OBSNightSkyShader +[Alias('moon_size')] +[ComponentModel.DefaultBindingProperty('moon_size')] +[Single] +$MoonSize, +# Set the moon_bump_size of OBSNightSkyShader +[Alias('moon_bump_size')] +[ComponentModel.DefaultBindingProperty('moon_bump_size')] +[Single] +$MoonBumpSize, +# Set the Moon_Position_x of OBSNightSkyShader +[Alias('Moon_Position_x')] +[ComponentModel.DefaultBindingProperty('Moon_Position_x')] +[Single] +$MoonPositionX, +# Set the Moon_Position_y of OBSNightSkyShader +[Alias('Moon_Position_y')] +[ComponentModel.DefaultBindingProperty('Moon_Position_y')] +[Single] +$MoonPositionY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'night_sky' +$ShaderNoun = 'OBSNightSkyShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Night Sky shader by Charles Fettinger for obs-shaderfilter plugin 6/2020 v.65 +// https://github.com/Oncorporation/obs-shaderfilter +//https://www.shadertoy.com/view/3tfXRM Simple Night Sky - converted from and updated +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 20.0; +uniform bool Include_Clouds = true; +uniform bool Include_Moon = true; +uniform int center_width_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "Center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 95.0; // +uniform bool Apply_To_Image = false; +uniform bool Replace_Image_Color = false; +uniform int number_stars< + string label = "Number stars"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; // -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform float4 SKY_COLOR = { 0.027, 0.151, 0.354, 1.0 }; +uniform float4 STAR_COLOR = { 0.92, 0.92, 0.14, 1.0 }; +uniform float4 LIGHT_SKY = { 0.45, 0.61, 0.98, 1.0 }; +uniform float SKY_LIGHTNESS< + string label = "SKY LIGHTNESS"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = .3; + // Moon +uniform float4 MOON_COLOR = { .4, .25, 0.25, 1.0 }; +uniform float moon_size< + string label = "Moon size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.18; +uniform float moon_bump_size< + string label = "Moon bump size"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.14; +uniform float Moon_Position_x< + string label = "Moon Position x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.6; +uniform float Moon_Position_y< + string label = "Moon Position y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.3; -process { +#define PI 3.1416 +//Noise functions from https://www.youtube.com/watch?v=zXsWftRdsvU +float noise11(float p) { + return frac(sin(p*633.1847) * 9827.95); +} + +float noise21(float2 p) { + return frac(sin(p.x*827.221 + p.y*3228.8275) * 878.121); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float2 noise22(float2 p) { + return frac(float2(sin(p.x*9378.35), sin(p.y*75.589)) * 556.89); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +//From https://codepen.io/Tobsta/post/procedural-generation-part-1-1d-perlin-noise +float cosineInterpolation(float a, float b, float x) { + float ft = x * PI; + float f = (1. - cos(ft)) * .5; + return a * (1. - f) + b * f; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float smoothNoise11(float p, float dist) { + float prev = noise11(p-dist); + float next = noise11(p+dist); + + return cosineInterpolation(prev, next, .5); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +float smoothNoise21(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + //smoothstep function: maybe change it later! + lv = lv*lv*(3.-2.*lv); + + float bl = noise21(id); + float br = noise21(id+float2(1.,0.)); + float b = lerp(bl, br, lv.x); + + float tl = noise21(id+float2(0.,1.)); + float tr = noise21(id+float2(1.,1.)); + float t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); +} +float2 smoothNoise22(float2 uv, float cells) { + float2 lv = frac(uv*cells); + float2 id = floor(uv*cells); + + lv = lv*lv*(3.-2.*lv); + + float2 bl = noise22(id); + float2 br = noise22(id+float2(1.,0.)); + float2 b = lerp(bl, br, lv.x); + + float2 tl = noise22(id+float2(0.,1.)); + float2 tr = noise22(id+float2(1.,1.)); + float2 t = lerp(tl, tr, lv.x); + + return lerp(b, t, lv.y); } +float valueNoise11(float p) { + float c = smoothNoise11(p, 0.5); + c += smoothNoise11(p, 0.25)*.5; + c += smoothNoise11(p, 0.125)*.25; + c += smoothNoise11(p, 0.0625)*.125; + + return c /= .875; +} -} +float valueNoise21(float2 uv) { + float c = smoothNoise21(uv, 4.); + c += smoothNoise21(uv, 8.)*.5; + c += smoothNoise21(uv, 16.)*.25; + c += smoothNoise21(uv, 32.)*.125; + c += smoothNoise21(uv, 64.)*.0625; + + return c /= .9375; +} - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputAudioTracks { +float2 valueNoise22(float2 uv) { + float2 c = smoothNoise22(uv, 4.); + c += smoothNoise22(uv, 8.)*.5; + c += smoothNoise22(uv, 16.)*.25; + c += smoothNoise22(uv, 32.)*.125; + c += smoothNoise22(uv, 64.)*.0625; + + return c /= .9375; +} +float3 points(float2 p, float2 uv, float3 color, float size, float blur) { + float dist = distance(p, uv); + return color*smoothstep(size, size*(0.999-blur), dist); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputAudioTracks')] -[Alias('obs.powershell.websocket.GetInputAudioTracks')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +float mapInterval(float x, float a, float b, float c, float d) { + return (x-a)/(b-a) * (d-c) + c; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float blink(float time, float timeInterval) { + float halfInterval = timeInterval / 2.0; + //Get relative position in the bucket + float p = fmod(time, timeInterval); + + + if (p <= timeInterval / 2.) { + return smoothstep(0., 1., p/halfInterval); + } else { + return smoothstep(1., 0., (p-halfInterval)/halfInterval); + } +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float3 sampleBumps(float2 p, float2 uv, float radius, float spin) { + float dist = distance(p, uv); + float3 BumpSamples = float3(0.,0.,0.); + + if (dist < radius) { + float bumps = (1.-valueNoise21(uv*spin))*.1; + BumpSamples = float3(bumps, bumps, bumps); + } + return BumpSamples; +} +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba;// = image.Sample(textureSampler, v_in.uv); + float alpha = clamp(Alpha_Percentage *.01 ,0,1.0); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01)); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st; -process { + // Normalized pixel coordinates (from 0 to 1) + float2 uv = v_in.uv; + float2 ouv = uv; + uv -= .5; + uv.x *= uv_size.x/uv_size.y; + + float2 seed = toCenter / uv_size.xy; + + float time = elapsed_time + seed.x*speed; + + //float3 col = float3(0.0); + //float m = valueNoise21(uv); + float3 col = lerp(SKY_COLOR.rgb, LIGHT_SKY.rgb, ouv.y-SKY_LIGHTNESS); + + col *= SKY_LIGHTNESS - (1.-ouv.y); + + //Add clouds + if (Include_Clouds) + { + float2 timeUv = uv; + timeUv.x += time*.1; + timeUv.y += valueNoise11(timeUv.x+.352)*.01; + float cloud = valueNoise21(timeUv); + col += cloud*.1; + } + //Add stars in the top part of the scene + float timeInterval = speed *.5; //5.0 + float timeBucket = floor(time / timeInterval); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float2 moonPosition = float2(-1000, -1000); + if (Include_Moon) + { + moonPosition = float2(Moon_Position_x, Moon_Position_y); + col += points(moonPosition, uv, MOON_COLOR.rgb,moon_size, 0.3); + // Moon bumps + col += sampleBumps(moonPosition, uv, moon_bump_size, 9. + fmod(time*.1,9)); + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + for (float i = 0.; i < clamp(number_stars,0,100); i++) { + float2 starPosition = float2(i/10., i/10.); + + starPosition.x = mapInterval(valueNoise11(timeBucket + i*827.913)-.4, 0., 1., 0.825, -0.825); + starPosition.y = mapInterval(valueNoise11(starPosition.x)-.3, 0., 1., 0.445, -0.445); + + float starIntensity = blink(time+ (rand_f * i), timeInterval ); + //Hide stars that are behind the moon + if (distance(starPosition, moonPosition) > moon_size) { + col += points(starPosition, uv, STAR_COLOR.rgb, 0.001, 0.0)*clamp(starIntensity-.1, 0.0, 1.0)*10.0; + col += points(starPosition, uv, STAR_COLOR.rgb, 0.009, 3.5)*starIntensity*3.0; } + } + //col = float3(blink(time, timeInterval)); + rgba = float4(col,alpha); - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + // Output to screen + if (Apply_To_Image) + { + float4 color = image.Sample(textureSampler, v_in.uv); + float4 original_color = color; + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color,alpha); + + } + return rgba; +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputDefaultSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputDefaultSettings')] -[Alias('obs.powershell.websocket.GetInputDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputKind')] -[string] -$InputKind, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52073,219 +45767,210 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputKind { - +function Get-OBSNoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputKindList')] -[Alias('obs.powershell.websocket.GetInputKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSNoiseShader','Add-OBSNoiseShader')] param( - +# Set the speed of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the scale of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# Set the noiseLevel of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('noiseLevel')] +[Single] +$NoiseLevel, +# Set the monochromatic of OBSNoiseShader +[ComponentModel.DefaultBindingProperty('monochromatic')] +[Management.Automation.SwitchParameter] +$Monochromatic, +# Set the use_rand of OBSNoiseShader +[Alias('use_rand')] +[ComponentModel.DefaultBindingProperty('use_rand')] +[Management.Automation.SwitchParameter] +$UseRand, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('unversioned')] -[switch] -$Unversioned, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'noise' +$ShaderNoun = 'OBSNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100; + float step = 0.1; +> = 1; +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 10; + float step = 0.0001; +> = 6; +uniform float noiseLevel< + string label = "Noise Level"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 1; + float step = 0.001; +> = 1; +uniform bool monochromatic = false; +uniform bool use_rand = false; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float rand(float2 st) +{ + return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123); } +float4 mainImage(VertData v_in) : TARGET +{ + float time = rand_activation_f + (speed / 60) * elapsed_time * 0.00001; -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputMute { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputMute')] -[Alias('obs.powershell.websocket.GetInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + if (use_rand) { + time = rand_f; + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float4 x; + if (monochromatic) { + x = rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + scale); + } else { + x = float4( + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 1 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 2 * scale), + rand(float2(v_in.uv.x, v_in.uv.y) * scale + time + 3 * scale), + 1 + ); + } -process { + float4 rgba = image.Sample(textureSampler, v_in.uv); + float3 output = lerp(rgba, x, noiseLevel); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return float4(output.r, output.g, output.b, rgba.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52294,229 +45979,254 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputPropertiesListPropertyItems { - +function Get-OBSNormalMapShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputPropertiesListPropertyItems')] -[Alias('obs.powershell.websocket.GetInputPropertiesListPropertyItems')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSNormalMapShader','Add-OBSNormalMapShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the strength of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# Set the offsetHeight of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('offsetHeight')] +[Management.Automation.SwitchParameter] +$OffsetHeight, +# Set the invertR of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertR')] +[Management.Automation.SwitchParameter] +$InvertR, +# Set the invertG of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertG')] +[Management.Automation.SwitchParameter] +$InvertG, +# Set the invertH of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('invertH')] +[Management.Automation.SwitchParameter] +$InvertH, +# Set the type of OBSNormalMapShader +[ComponentModel.DefaultBindingProperty('type')] +[Int32] +$Type, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'normal_map' +$ShaderNoun = 'OBSNormalMapShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Normal map shader based on cpetry''s NormalMap-Online website +// https://github.com/cpetry/NormalMap-Online/blob/gh-pages/javascripts/shader/NormalMapShader.js +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.001; + float maximum = 10; + float step = 0.001; +> = 1; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform bool offsetHeight = true; +uniform bool invertR = false; +uniform bool invertG = false; +uniform bool invertH = false; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform int type< + string label = "Filter Type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Sobel"; + int option_1_value = 1; + string option_1_label = "Scharr"; +> = 0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float4 mainImage( VertData v_in ) : TARGET { + float2 step = float2(1.0, 1.0) / uv_size; + float2 vUv = v_in.uv; -} + float dz = 1 / strength; + float dz2 = dz * dz; + float2 tlv = float2(vUv.x - step.x, vUv.y + step.y); + float2 lv = float2(vUv.x - step.x, vUv.y ); + float2 blv = float2(vUv.x - step.x, vUv.y - step.y); + float2 tv = float2(vUv.x , vUv.y + step.y); + float2 bv = float2(vUv.x , vUv.y - step.y); + float2 trv = float2(vUv.x + step.x, vUv.y + step.y); + float2 rv = float2(vUv.x + step.x, vUv.y ); + float2 brv = float2(vUv.x + step.x, vUv.y - step.y); -} + tlv = float2(tlv.x >= 0.0 ? tlv.x : (1.0 + tlv.x), tlv.y >= 0.0 ? tlv.y : (1.0 + tlv.y)); + tlv = float2(tlv.x < 1.0 ? tlv.x : (tlv.x - 1.0 ), tlv.y < 1.0 ? tlv.y : (tlv.y - 1.0 )); + lv = float2( lv.x >= 0.0 ? lv.x : (1.0 + lv.x), lv.y >= 0.0 ? lv.y : (1.0 + lv.y)); + lv = float2( lv.x < 1.0 ? lv.x : ( lv.x - 1.0 ), lv.y < 1.0 ? lv.y : ( lv.y - 1.0 )); + blv = float2(blv.x >= 0.0 ? blv.x : (1.0 + blv.x), blv.y >= 0.0 ? blv.y : (1.0 + blv.y)); + blv = float2(blv.x < 1.0 ? blv.x : (blv.x - 1.0 ), blv.y < 1.0 ? blv.y : (blv.y - 1.0 )); + tv = float2( tv.x >= 0.0 ? tv.x : (1.0 + tv.x), tv.y >= 0.0 ? tv.y : (1.0 + tv.y)); + tv = float2( tv.x < 1.0 ? tv.x : ( tv.x - 1.0 ), tv.y < 1.0 ? tv.y : ( tv.y - 1.0 )); + bv = float2( bv.x >= 0.0 ? bv.x : (1.0 + bv.x), bv.y >= 0.0 ? bv.y : (1.0 + bv.y)); + bv = float2( bv.x < 1.0 ? bv.x : ( bv.x - 1.0 ), bv.y < 1.0 ? bv.y : ( bv.y - 1.0 )); + trv = float2(trv.x >= 0.0 ? trv.x : (1.0 + trv.x), trv.y >= 0.0 ? trv.y : (1.0 + trv.y)); + trv = float2(trv.x < 1.0 ? trv.x : (trv.x - 1.0 ), trv.y < 1.0 ? trv.y : (trv.y - 1.0 )); + rv = float2( rv.x >= 0.0 ? rv.x : (1.0 + rv.x), rv.y >= 0.0 ? rv.y : (1.0 + rv.y)); + rv = float2( rv.x < 1.0 ? rv.x : ( rv.x - 1.0 ), rv.y < 1.0 ? rv.y : ( rv.y - 1.0 )); + brv = float2(brv.x >= 0.0 ? brv.x : (1.0 + brv.x), brv.y >= 0.0 ? brv.y : (1.0 + brv.y)); + brv = float2(brv.x < 1.0 ? brv.x : (brv.x - 1.0 ), brv.y < 1.0 ? brv.y : (brv.y - 1.0 )); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputSettings { + float tl = image.Sample(textureSampler, tlv).r; + float l = image.Sample(textureSampler, lv ).r; + float bl = image.Sample(textureSampler, blv).r; + float t = image.Sample(textureSampler, tv ).r; + float b = image.Sample(textureSampler, bv ).r; + float tr = image.Sample(textureSampler, trv).r; + float r = image.Sample(textureSampler, rv ).r; + float br = image.Sample(textureSampler, brv).r; + float dx = 0.0; + float dy = 0.0; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputSettings')] -[Alias('obs.powershell.websocket.GetInputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + if(type == 0) { // Sobel + dx = tl + l*2.0 + bl - tr - r*2.0 - br; + dy = tl + t*2.0 + tr - bl - b*2.0 - br; + } + else { // Scharr + dx = tl*3.0 + l*10.0 + bl*3.0 - tr*3.0 - r*10.0 - br*3.0; + dy = tl*3.0 + t*10.0 + tr*3.0 - bl*3.0 - b*10.0 - br*3.0; + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + float invH = invertH ? -1. : 1.; + float invR = invertR ? -1. : 1.; + float invG = invertG ? -1. : 1.; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float4 normal = float4( + float3(dx * invR * invH, dy * invG * invH, dz), + image.Sample(textureSampler, vUv).a + ); + l = sqrt((dx * dx) + (dy * dy) + dz2); -process { + if (offsetHeight) { + return float4(normal.xy / l * 0.5 + 0.5, normal.zw); + } + return float4(normal.xyz / l * 0.5 + 0.5, normal.w); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52525,111 +46235,155 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSInputVolume { - +function Get-OBSOpacityShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetInputVolume')] -[Alias('obs.powershell.websocket.GetInputVolume')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSOpacityShader','Add-OBSOpacityShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the Opacity of OBSOpacityShader +[ComponentModel.DefaultBindingProperty('Opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'opacity' +$ShaderNoun = 'OBSOpacityShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Opacity shader - for OBS Shaderfilter +// Copyright 2021 by SkeltonBowTV +// https://twitter.com/skeletonbowtv +// https://twitch.tv/skeletonbowtv +uniform float Opacity< + string label = "Opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.01; +> = 100.00; // 0.00 - 100.00 percent - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage( VertData v_in ) : TARGET +{ + float4 color = image.Sample( textureSampler, v_in.uv ); + return float4( color.rgb, color.a * Opacity * 0.01 ); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52638,101 +46392,217 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSLastReplayBufferReplay { - +function Get-OBSPagePeelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetLastReplayBufferReplay')] -[Alias('obs.powershell.websocket.GetLastReplayBufferReplay')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPagePeelShader','Add-OBSPagePeelShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Speed of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Position of OBSPagePeelShader +[ComponentModel.DefaultBindingProperty('Position')] +[Single] +$Position, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'page-peel' +$ShaderNoun = 'OBSPagePeelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Page Peel, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2023 by SkeletonBow +// License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License +// Contact info: +// Twitter: +// Twitch: +// YouTube: +// Soundcloud: +// +// Based on Shadertoy shader by droozle +// +// Description: +// +// +// Changelog: +// 0.01 - Initial release +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 50.0; + float step = 0.001; +> = 1.00; +uniform float Position< + string label = "Position"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage( VertData v_in ) : TARGET +{ + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); + float2 uv = v_in.uv; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + float t = Position + elapsed_time * Speed; + // Define the fold. + float2 origin = float2( 0.6 + 0.4 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); + + // Sample texture. + float3 col = image.Sample( textureSampler, uv ).rgb; // Front color. + + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + col *= 0.25; // Background color (peeled off). + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); + } + else { + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + float4 back = image.Sample( textureSampler, pt ); // Back color. + back.rgb = back.rgb * 0.25 + 0.75; + + float shadow = smoothstep( 0.0, 0.2, -side ); + back.rgb = lerp( back.rgb * 0.2, back.rgb, shadow ); + + // Support for transparency. + col = lerp( col, back.rgb, back.a ); } + } + + // Output to screen + return float4(col,1.0); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52741,214 +46611,230 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSMediaInputStatus { - +function Get-OBSPagePeelTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMediaInputStatus')] -[Alias('obs.powershell.websocket.GetMediaInputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPagePeelTransitionShader','Add-OBSPagePeelTransitionShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the image_a of OBSPagePeelTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSPagePeelTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSPagePeelTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSPagePeelTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the page_color of OBSPagePeelTransitionShader +[Alias('page_color')] +[ComponentModel.DefaultBindingProperty('page_color')] +[String] +$PageColor, +# Set the page_transparency of OBSPagePeelTransitionShader +[Alias('page_transparency')] +[ComponentModel.DefaultBindingProperty('page_transparency')] +[Single] +$PageTransparency, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'page-peel-transition' +$ShaderNoun = 'OBSPagePeelTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float4 page_color = {1.0, 1.0, 1.0, 1.0}; +uniform float page_transparency< + string label = "Page Transparency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + // Normalized pixel coordinates (from 0 to 1) + float2 aspect = float2( uv_size.x / uv_size.y, 1.0 ); + float2 uv = v_in.uv; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float t = transition_time * 12.0 + 11.0; + // Define the fold. + float2 origin = float2( 0.6 + 0.6 * sin( t * 0.2 ), 0.5 + 0.5 * cos( t * 0.13 ) ) * aspect; + float2 normal = normalize( float2( 1.0, 2.0 * sin( t * 0.3 ) ) * aspect ); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + // Sample texture. + float3 col = float3(1.0,0.0,0.0); + + // Check on which side the pixel lies. + float2 pt = uv * aspect - origin; + float side = dot( pt, normal ); + if( side > 0.0 ) { + // Next page + col = image_b.Sample( textureSampler, uv ).rgb; + + float shadow = smoothstep( 0.0, 0.05, side ); + col = lerp( col * 0.6, col, shadow ); + } + else { + - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + // Find the mirror pixel. + pt = ( uv * aspect - 2.0 * side * normal ) / aspect; + + // Check if we''re still inside the image bounds. + if( pt.x >= 0.0 && pt.x < 1.0 && pt.y >= 0.0 && pt.y < 1.0 ) { + col = image_a.Sample( textureSampler, pt ).rgb; // Back color. + col = lerp(page_color.rgb, col, page_transparency); + + float shadow = smoothstep( 0.0, 0.2, -side ); + col = lerp( col * 0.2, col, shadow ); + }else{ + col = image_a.Sample( textureSampler, uv ).rgb; } + } + + // Output to screen + if(convert_linear) + col = srgb_nonlinear_to_linear(col); + return float4(col,1.0); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSMonitor { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetMonitorList')] -[Alias('obs.powershell.websocket.GetMonitorList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -52957,209 +46843,429 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutput { - +function Get-OBSPerlinNoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputList')] -[Alias('obs.powershell.websocket.GetOutputList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPerlinNoiseShader','Add-OBSPerlinNoiseShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the animated of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('animated')] +[Management.Automation.SwitchParameter] +$Animated, +# Set the apply_to_channel of OBSPerlinNoiseShader +[Alias('apply_to_channel')] +[ComponentModel.DefaultBindingProperty('apply_to_channel')] +[Management.Automation.SwitchParameter] +$ApplyToChannel, +# Set the inverted of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('inverted')] +[Management.Automation.SwitchParameter] +$Inverted, +# Set the multiply of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('multiply')] +[Management.Automation.SwitchParameter] +$Multiply, +# Set the speed_horizonal of OBSPerlinNoiseShader +[Alias('speed_horizonal')] +[ComponentModel.DefaultBindingProperty('speed_horizonal')] +[Single] +$SpeedHorizonal, +# Set the speed_vertical of OBSPerlinNoiseShader +[Alias('speed_vertical')] +[ComponentModel.DefaultBindingProperty('speed_vertical')] +[Single] +$SpeedVertical, +# Set the iterations of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('iterations')] +[Int32] +$Iterations, +# Set the white_noise of OBSPerlinNoiseShader +[Alias('white_noise')] +[ComponentModel.DefaultBindingProperty('white_noise')] +[Single] +$WhiteNoise, +# Set the black_noise of OBSPerlinNoiseShader +[Alias('black_noise')] +[ComponentModel.DefaultBindingProperty('black_noise')] +[Single] +$BlackNoise, +# Set the notes of OBSPerlinNoiseShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'perlin_noise' +$ShaderNoun = 'OBSPerlinNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// +// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +// +// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson +// Translation and modification was made by Keijiro Takahashi. +// Conversion for OBS by Charles Fettinger. +// +// This shader is based on the webgl-noise GLSL shader. For further details +// of the original shader, please see the following description from the +// original source code. +// + // +// GLSL textureless classic 2D noise "cnoise", (white_noise) +// with an RSL-style periodic variant "pnoise" (black_noise). +// Author: Stefan Gustavson (stefan.gustavson@liu.se) +// Version: 2011-08-22 +// +// Many thanks to Ian McEwan of Ashima Arts for the +// ideas for permutation and gradient selection. +// +// Copyright (c) 2011 Stefan Gustavson. All rights reserved. +// Distributed under the MIT license. See LICENSE file. +// https://github.com/ashima/webgl-noise +//Converted to OpenGL by Q-mii & Exeldro March 8, 2022 + float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + float4 mod289(float4 x) +{ + return x - floor(x / 289.0) * 289.0; +} + float4 permute(float4 x) +{ + return mod289(((x*34.0)+1.0)*x); +} + float4 taylorInvSqrt(float4 r) +{ + return 1.79284291400159 - r * 0.85373472095314; +} + float2 fade(float2 t) { + return t*t*t*(t*(t*6.0-15.0)+10.0); +} + // Classic Perlin noise +float cnoise(float2 P) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + // Classic Perlin noise, periodic variant +float pnoise(float2 P, float2 rep) +{ + float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0); + float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, rep.xyxy); // To create noise with explicit period + Pi = mod289(Pi); // To avoid truncation effects in permutation + float4 ix = Pi.xzxz; + float4 iy = Pi.yyww; + float4 fx = Pf.xzxz; + float4 fy = Pf.yyww; + float4 i = permute(permute(ix) + iy); + float4 gx = frac(i / 41.0) * 2.0 - 1.0 ; + float4 gy = abs(gx) - 0.5 ; + float4 tx = floor(gx + 0.5); + gx = gx - tx; + float2 g00 = float2(gx.x,gy.x); + float2 g10 = float2(gx.y,gy.y); + float2 g01 = float2(gx.z,gy.z); + float2 g11 = float2(gx.w,gy.w); + float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + float n00 = dot(g00, float2(fx.x, fy.x)); + float n10 = dot(g10, float2(fx.y, fy.y)); + float n01 = dot(g01, float2(fx.z, fy.z)); + float n11 = dot(g11, float2(fx.w, fy.w)); + float2 fade_xy = fade(Pf.xy); + float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x); + float n_xy = lerp(n_x.x, n_x.y, fade_xy.y); + return 2.3 * n_xy; +} + //The good bits~ adapting the noise generator for the plugin and giving some control over the shader + //todo: pseudorandom number generator w/ seed +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform bool animated; +uniform bool apply_to_channel; +uniform bool inverted; +uniform bool multiply; +uniform float speed_horizonal< + string label = "Speed horizontal"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform float speed_vertical< + string label = "Speed vertical"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0; +uniform int iterations< + string label = "Iterations"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 20; + int step = 1; +> = 4; +//how much c_noise do we want? white +uniform float white_noise< + string label = "White noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +//how much p_noise do we want? black +uniform float black_noise< + string label = "Black noise"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.5; +uniform string notes< + string widget_type = "info"; +> = "white noise and black noise and iterations.. enjoy!"; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + float2 noisePosition(float t){ + return float2(sin(2.2 * t) - cos(1.4 * t), cos(1.3 * t) + sin(-1.9 *t)); +} + float4 mainImage(VertData v_in) : TARGET +{ + float4 color = image.Sample(textureSampler, v_in.uv); + float t = elapsed_time * speed; + float2 dir = float2(speed_horizonal,speed_vertical); + + if(!animated){ + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = v_in.uv * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + //o += pnoise(coord, period) * w; + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } + } else { + float o = 0.5; + float scale = 1.0; + float w = 0.5; + for(int i = 0; i < iterations; i++){ + float2 coord = (v_in.uv + t*dir) * scale; + float2 period = float2(scale * 2.0, scale * 2.0); + + if(white_noise == 0.0 && black_noise == 0.0){ + o += pnoise(coord, period) * w; + } else { + if(white_noise != 0.0){ + o += cnoise(coord) * w * white_noise; + } + if(black_noise != 0.0){ + o += pnoise(coord, period) * w * black_noise; + } + } + + scale *= 2.0; + w *= 0.5; + } + if(inverted){ + o = 1 - o; + } + if(apply_to_channel){ + if(multiply){ + return float4(color.r,color.g,color.b,color.a*o); + } else { + return float4(color.r,color.g,color.b,o); + } + } else { + return float4(o,o,o,1.0); + } + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputSettings')] -[Alias('obs.powershell.websocket.GetOutputSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53168,322 +47274,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSOutputStatus { - +function Get-OBSPerspectiveShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetOutputStatus')] -[Alias('obs.powershell.websocket.GetOutputStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPerspectiveShader','Add-OBSPerspectiveShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the angle_x of OBSPerspectiveShader +[Alias('angle_x')] +[ComponentModel.DefaultBindingProperty('angle_x')] +[Single] +$AngleX, +# Set the angle_y of OBSPerspectiveShader +[Alias('angle_y')] +[ComponentModel.DefaultBindingProperty('angle_y')] +[Single] +$AngleY, +# Set the angle_z of OBSPerspectiveShader +[Alias('angle_z')] +[ComponentModel.DefaultBindingProperty('angle_z')] +[Single] +$AngleZ, +# Set the perspective of OBSPerspectiveShader +[ComponentModel.DefaultBindingProperty('perspective')] +[Single] +$Perspective, +# Set the border_color of OBSPerspectiveShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the show_border of OBSPerspectiveShader +[Alias('show_border')] +[ComponentModel.DefaultBindingProperty('show_border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'perspective' +$ShaderNoun = 'OBSPerspectiveShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Perspective Transform Shader for OBS +// Allows adjustable 3D perspective effects +// Usage: Add as filter in OBS via ShaderFilter plugin +uniform float angle_x< + string label = "X Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float angle_y< + string label = "Y Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float angle_z< + string label = "Z Rotation"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 1.0; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float perspective< + string label = "Perspective Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float4 border_color< + string label = "Border Color"; +> = {0.0, 0.0, 0.0, 1.0}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform bool show_border< + string label = "Show Border"; +> = true; +float4x4 rotationMatrix(float3 angles) +{ + float radX = radians(angles.x); + float radY = radians(angles.y); + float radZ = radians(angles.z); + + float sinX = sin(radX); + float cosX = cos(radX); + float sinY = sin(radY); + float cosY = cos(radY); + float sinZ = sin(radZ); + float cosZ = cos(radZ); + + return float4x4( + cosY*cosZ, -cosY*sinZ, sinY, 0, + sinX*sinY*cosZ + cosX*sinZ, -sinX*sinY*sinZ + cosX*cosZ, -sinX*cosY, 0, + -cosX*sinY*cosZ + sinX*sinZ, cosX*sinY*sinZ + sinX*cosZ, cosX*cosY, 0, + 0, 0, 0, 1 + ); } - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSPersistentData { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetPersistentData')] -[Alias('obs.powershell.websocket.GetPersistentData')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + + // Center coordinates + float2 center = float2(0.5, 0.5); + uv -= center; + + // Apply perspective + float perspectiveFactor = 1.0 / (1.0 + perspective * length(uv)); + uv *= perspectiveFactor; + + // Create rotation matrix + float3 angles = float3(angle_x, angle_y, angle_z); + float4x4 rotMat = rotationMatrix(angles); + + // Apply transformation + float4 transformed = mul(rotMat, float4(uv.x, uv.y, 0, 1)); + + // Restore center position + uv = transformed.xy + center; + + // Sample texture with border handling + if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { + return show_border ? border_color : float4(0, 0, 0, 0); + } + + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfile { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileList')] -[Alias('obs.powershell.websocket.GetProfileList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53492,214 +47532,402 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSProfileParameter { - +function Get-OBSPieChartShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetProfileParameter')] -[Alias('obs.powershell.websocket.GetProfileParameter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPieChartShader','Add-OBSPieChartShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the inner_radius of OBSPieChartShader +[Alias('inner_radius')] +[ComponentModel.DefaultBindingProperty('inner_radius')] +[Single] +$InnerRadius, +# Set the outer_radius of OBSPieChartShader +[Alias('outer_radius')] +[ComponentModel.DefaultBindingProperty('outer_radius')] +[Single] +$OuterRadius, +# Set the start_angle of OBSPieChartShader +[Alias('start_angle')] +[ComponentModel.DefaultBindingProperty('start_angle')] +[Single] +$StartAngle, +# Set the total of OBSPieChartShader +[ComponentModel.DefaultBindingProperty('total')] +[Int32] +$Total, +# Set the part_1 of OBSPieChartShader +[Alias('part_1')] +[ComponentModel.DefaultBindingProperty('part_1')] +[Int32] +$Part1, +# Set the color_1 of OBSPieChartShader +[Alias('color_1')] +[ComponentModel.DefaultBindingProperty('color_1')] +[String] +$Color1, +# Set the part_2 of OBSPieChartShader +[Alias('part_2')] +[ComponentModel.DefaultBindingProperty('part_2')] +[Int32] +$Part2, +# Set the color_2 of OBSPieChartShader +[Alias('color_2')] +[ComponentModel.DefaultBindingProperty('color_2')] +[String] +$Color2, +# Set the part_3 of OBSPieChartShader +[Alias('part_3')] +[ComponentModel.DefaultBindingProperty('part_3')] +[Int32] +$Part3, +# Set the color_3 of OBSPieChartShader +[Alias('color_3')] +[ComponentModel.DefaultBindingProperty('color_3')] +[String] +$Color3, +# Set the part_4 of OBSPieChartShader +[Alias('part_4')] +[ComponentModel.DefaultBindingProperty('part_4')] +[Int32] +$Part4, +# Set the color_4 of OBSPieChartShader +[Alias('color_4')] +[ComponentModel.DefaultBindingProperty('color_4')] +[String] +$Color4, +# Set the part_5 of OBSPieChartShader +[Alias('part_5')] +[ComponentModel.DefaultBindingProperty('part_5')] +[Int32] +$Part5, +# Set the color_5 of OBSPieChartShader +[Alias('color_5')] +[ComponentModel.DefaultBindingProperty('color_5')] +[String] +$Color5, +# Set the part_6 of OBSPieChartShader +[Alias('part_6')] +[ComponentModel.DefaultBindingProperty('part_6')] +[Int32] +$Part6, +# Set the color_6 of OBSPieChartShader +[Alias('color_6')] +[ComponentModel.DefaultBindingProperty('color_6')] +[String] +$Color6, +# Set the part_7 of OBSPieChartShader +[Alias('part_7')] +[ComponentModel.DefaultBindingProperty('part_7')] +[Int32] +$Part7, +# Set the color_7 of OBSPieChartShader +[Alias('color_7')] +[ComponentModel.DefaultBindingProperty('color_7')] +[String] +$Color7, +# Set the part_8 of OBSPieChartShader +[Alias('part_8')] +[ComponentModel.DefaultBindingProperty('part_8')] +[Int32] +$Part8, +# Set the color_8 of OBSPieChartShader +[Alias('color_8')] +[ComponentModel.DefaultBindingProperty('color_8')] +[String] +$Color8, +# Set the part_9 of OBSPieChartShader +[Alias('part_9')] +[ComponentModel.DefaultBindingProperty('part_9')] +[Int32] +$Part9, +# Set the color_9 of OBSPieChartShader +[Alias('color_9')] +[ComponentModel.DefaultBindingProperty('color_9')] +[String] +$Color9, +# Set the part_10 of OBSPieChartShader +[Alias('part_10')] +[ComponentModel.DefaultBindingProperty('part_10')] +[Int32] +$Part10, +# Set the color_10 of OBSPieChartShader +[Alias('color_10')] +[ComponentModel.DefaultBindingProperty('color_10')] +[String] +$Color10, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pie-chart' +$ShaderNoun = 'OBSPieChartShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float inner_radius< + string label = "inner radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 32.0; +uniform float outer_radius< + string label = "outer radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 50.0; +uniform float start_angle< + string label = "Start angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 90.0; +uniform int total< + string label = "Total"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 100; +uniform int part_1< + string label = "Part 1"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 50; +uniform float4 color_1 = {0.0,0.26,0.62,1.0}; +uniform int part_2< + string label = "Part 2"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 25; +uniform float4 color_2 = {0.24,0.40,0.68,1.0}; +uniform int part_3< + string label = "Part 3"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 10; +uniform float4 color_3 = {0.38,0.56,0.75,1.0}; +uniform int part_4< + string label = "Part 4"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 5; +uniform float4 color_4 = {0.52,0.72,0.81,1.0}; +uniform int part_5< + string label = "Part 5"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 3; +uniform float4 color_5 = {0.69,0.87,0.86,1.0}; +uniform int part_6< + string label = "Part 6"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 2; +uniform float4 color_6 = {1.0,0.79,0.73,1.0}; +uniform int part_7< + string label = "Part 7"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_7 = {0.99,0.57,0.57,1.0}; +uniform int part_8< + string label = "Part 8"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_8 = {0.91,0.36,0.44,1.0}; +uniform int part_9< + string label = "Part 9"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 1; +uniform float4 color_9 = {0.77,0.16,0.32,1.0}; +uniform int part_10< + string label = "Part 10"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 1000; + int step = 1; +> = 0; +uniform float4 color_10 = {0.58,0.0,0.23,1.0}; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + const float pi = 3.14159265358979323846; +#ifdef OPENGL + float[10] parts = float[10](part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10); + float4[10] colors = float4[10](color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10); +#else + float parts[] = {part_1, part_2, part_3, part_4, part_5, part_6, part_7, part_8, part_9, part_10}; + float4 colors[] = {color_1, color_2, color_3, color_4, color_5, color_6, color_7, color_8, color_9, color_10}; +#endif + float2 center = float2(0.5, 0.5); + float2 factor; + if(uv_size.x < uv_size.y){ + factor = float2(1.0, uv_size.y/uv_size.x); + }else{ + factor = float2(uv_size.x/uv_size.y, 1.0); + } + center = center * factor; + float d = distance(center, v_in.uv * factor); + if(d > outer_radius/100.0 || d < inner_radius/100.0){ + return image.Sample(textureSampler, v_in.uv); + } + float2 toCenter = center - v_in.uv*factor; + float angle = atan2(toCenter.y ,toCenter.x); + angle = angle - (start_angle / 180.0 * pi); + if(angle < 0.0) + angle = pi + pi + angle; + if(angle < 0.0) + angle = pi + pi + angle; + angle = angle / (pi + pi); + float t = 0.0; + for(int i = 0; i < 10; i+=1) { + float part = parts[i]/total; + if(angle > t && angle <= t+part){ + return colors[i]; } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + t = t + part; + } + return image.Sample(textureSampler, v_in.uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordDirectory { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordDirectory')] -[Alias('obs.powershell.websocket.GetRecordDirectory')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53708,204 +47936,192 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSRecordStatus { - +function Get-OBSPixelationShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetRecordStatus')] -[Alias('obs.powershell.websocket.GetRecordStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPixelationShader','Add-OBSPixelationShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Target_Width of OBSPixelationShader +[Alias('Target_Width')] +[ComponentModel.DefaultBindingProperty('Target_Width')] +[Single] +$TargetWidth, +# Set the Target_Height of OBSPixelationShader +[Alias('Target_Height')] +[ComponentModel.DefaultBindingProperty('Target_Height')] +[Single] +$TargetHeight, +# Set the notes of OBSPixelationShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pixelation' +$ShaderNoun = 'OBSPixelationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// pixelation shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// with help from SkeltonBowTV +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 15, 2022 +uniform float Target_Width< + string label = "Target Width"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 320.0; +uniform float Target_Height< + string label = "Target Height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2000.0; + float step = 0.1; +> = 180.0; +uniform string notes< + string widget_type = "info"; +> = "adjust width and height to your screen dimension"; +float4 mainImage(VertData v_in) : TARGET +{ + float targetWidth = Target_Width; + if(targetWidth < 2.0) + targetWidth = 2.0; + float targetHeight = Target_Height; + if(targetHeight < 2.0) + targetHeight = 2.0; + float2 tex1; + int pixelSizeX = int(uv_size.x / targetWidth); + int pixelSizeY = int(uv_size.y / targetHeight); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + int pixelX = int(v_in.uv.x * uv_size.x); + int pixelY = int(v_in.uv.y * uv_size.y); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + tex1.x = ((float(pixelX / pixelSizeX)*float(pixelSizeX)) / uv_size.x) + (float(pixelSizeX) / uv_size.x)/2.0; + tex1.y = ((float(pixelY / pixelSizeY)*float(pixelSizeY)) / uv_size.y) + (float(pixelSizeY) / uv_size.y)/2.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + float4 c1 = image.Sample(textureSampler, tex1 ); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + return c1; +} - if ($PassThru) { - [PSCustomObject]$requestPayload +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSReplayBufferStatus { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetReplayBufferStatus')] -[Alias('obs.powershell.websocket.GetReplayBufferStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -53914,101 +48130,210 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSScene { - +function Get-OBSPixelationTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneList')] -[Alias('obs.powershell.websocket.GetSceneList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPixelationTransitionShader','Add-OBSPixelationTransitionShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the transition_time of OBSPixelationTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSPixelationTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the power of OBSPixelationTransitionShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the center_x of OBSPixelationTransitionShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSPixelationTransitionShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pixelation-transition' +$ShaderNoun = 'OBSPixelationTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float transition_time< + string label = "Transittion Time"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform bool convert_linear = true; +uniform float power< + string label = "Power"; + string widget_type = "slider"; + float minimum = 0.5; + float maximum = 8.0; + float step = 0.01; +> = 3.0; +uniform float center_x< + string label = "X"; + string widget_type = "slider"; + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Y"; + string widget_type = "slider"; + string group = "Center"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + //1..0..1 + float scale = abs(transition_time - 0.5) * 2.0; + scale = pow(scale, power); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float2 uv = v_in.uv; + uv -= float2(center_x, center_y); + uv *= uv_size; + uv *= scale; + uv = floor(uv); + uv /= scale; + uv /= uv_size; + uv += float2(center_x, center_y); + uv = clamp(uv, 1.0/uv_size, 1.0); + float4 rgba = image.Sample(textureSampler, uv); + if(convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54017,101 +48342,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneCollection { - +function Get-OBSPolarShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneCollectionList')] -[Alias('obs.powershell.websocket.GetSceneCollectionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPolarShader','Add-OBSPolarShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the center_x of OBSPolarShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSPolarShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the point_y of OBSPolarShader +[Alias('point_y')] +[ComponentModel.DefaultBindingProperty('point_y')] +[Single] +$PointY, +# Set the flip of OBSPolarShader +[ComponentModel.DefaultBindingProperty('flip')] +[Management.Automation.SwitchParameter] +$Flip, +# Set the rotate of OBSPolarShader +[ComponentModel.DefaultBindingProperty('rotate')] +[Single] +$Rotate, +# Set the repeat of OBSPolarShader +[ComponentModel.DefaultBindingProperty('repeat')] +[Single] +$Repeat, +# Set the scale of OBSPolarShader +[ComponentModel.DefaultBindingProperty('scale')] +[Single] +$Scale, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'polar' +$ShaderNoun = 'OBSPolarShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +#define PI 3.14159265359 +#define PI_2 6.2831 +#define mod(x,y) (x - y * floor(x / y)) + +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float point_y< + string label = "Point y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool flip; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float rotate< + string label = "Rotate"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } +uniform float repeat< + string label = "Repeat"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; + +uniform float scale< + string label = "Scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv.x -= center_x ; + uv.y -= center_y ; + uv.x = uv.x * ( uv_size.x / uv_size.y); + float pixel_angle = atan2(uv.x,uv.y)/PI_2+0.5; + if(repeat < 1.0){ + pixel_angle = mod(pixel_angle+rotate,1.0); + if(pixel_angle > repeat) + return float4(0,0,0,0); + pixel_angle = mod(pixel_angle/repeat,1.0); + } else { + pixel_angle = mod(pixel_angle*repeat+rotate, 1.0); + } + float pixel_distance = length(uv)/ scale - point_y; + float2 uv2 = float2(pixel_angle , pixel_distance); + if(flip) + uv2 = float2(1.0,1.0) - uv2; + return image.Sample(textureSampler,uv2); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54120,111 +48582,259 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItem { - +function Get-OBSPulseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemList')] -[Alias('obs.powershell.websocket.GetSceneItemList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSPulseShader','Add-OBSPulseShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the ViewProj of OBSPulseShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSPulseShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSPulseShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSPulseShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSPulseShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSPulseShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSPulseShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSPulseShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed of OBSPulseShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the min_growth_pixels of OBSPulseShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] +[Single] +$MinGrowthPixels, +# Set the max_growth_pixels of OBSPulseShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'pulse' +$ShaderNoun = 'OBSPulseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 0.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 200.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + + float3 pos = v_in.pos.xyz; + float3 direction_from_center = float3((v_in.uv.x - 0.5) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5, 0); + float3 min_pos = pos + direction_from_center * min_growth_pixels / 2; + float3 max_pos = pos + direction_from_center * max_growth_pixels / 2; + + float t = (1 + sin(elapsed_time * speed)) / 2; + float3 current_pos = min_pos * (1 - t) + max_pos * t; + + vert_out.pos = mul(float4(current_pos, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; +} + +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54233,117 +48843,247 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemBlendMode { - +function Get-OBSQuadrilateralCropShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.GetSceneItemBlendMode')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSQuadrilateralCropShader','Add-OBSQuadrilateralCropShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Top_Left_X of OBSQuadrilateralCropShader +[Alias('Top_Left_X')] +[ComponentModel.DefaultBindingProperty('Top_Left_X')] +[Single] +$TopLeftX, +# Set the Top_Left_Y of OBSQuadrilateralCropShader +[Alias('Top_Left_Y')] +[ComponentModel.DefaultBindingProperty('Top_Left_Y')] +[Single] +$TopLeftY, +# Set the Top_Right_X of OBSQuadrilateralCropShader +[Alias('Top_Right_X')] +[ComponentModel.DefaultBindingProperty('Top_Right_X')] +[Single] +$TopRightX, +# Set the Top_Right_Y of OBSQuadrilateralCropShader +[Alias('Top_Right_Y')] +[ComponentModel.DefaultBindingProperty('Top_Right_Y')] +[Single] +$TopRightY, +# Set the Bottom_Left_X of OBSQuadrilateralCropShader +[Alias('Bottom_Left_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_X')] +[Single] +$BottomLeftX, +# Set the Bottom_Left_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Left_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Left_Y')] +[Single] +$BottomLeftY, +# Set the Bottom_Right_X of OBSQuadrilateralCropShader +[Alias('Bottom_Right_X')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_X')] +[Single] +$BottomRightX, +# Set the Bottom_Right_Y of OBSQuadrilateralCropShader +[Alias('Bottom_Right_Y')] +[ComponentModel.DefaultBindingProperty('Bottom_Right_Y')] +[Single] +$BottomRightY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) -process { +process { +$shaderName = 'quadrilateral_crop' +$ShaderNoun = 'OBSQuadrilateralCropShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Quadrilateral Crop shader (inverse of a corner pin): transform a 4 points polygon to the corners of the source. +// Useful to revert perspective. + +uniform float Top_Left_X< + string label = "Top Left X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Left_Y< + string label = "Top Left Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Top_Right_X< + string label = "Top Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Top_Right_Y< + string label = "Top Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_X< + string label = "Bottom Left X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 0; +uniform float Bottom_Left_Y< + string label = "Bottom Left Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_X< + string label = "Bottom Right X"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; +uniform float Bottom_Right_Y< + string label = "Bottom Right Y"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 100.0; + float step = 0.01; +> = 100.; + +float4 mainImage( VertData v_in ) : TARGET { + + float2 tl = float2(Top_Left_X, Top_Left_Y) * .01; + float2 tr = float2(Top_Right_X, Top_Right_Y) * .01; + float2 bl = float2(Bottom_Left_X, Bottom_Left_Y) * .01; + float2 br = float2(Bottom_Right_X, Bottom_Right_Y) * .01; + + float2 t = lerp(tl, tr, v_in.uv[0]); + float2 b = lerp(bl, br, v_in.uv[0]); + float2 uv = lerp(t, b, v_in.uv[1]); + return image.Sample(textureSampler, uv); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54352,241 +49092,331 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemEnabled { - +function Get-OBSRainbowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemEnabled')] -[Alias('obs.powershell.websocket.GetSceneItemEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRainbowShader','Add-OBSRainbowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Saturation of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Saturation')] +[Single] +$Saturation, +# Set the Luminosity of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Luminosity')] +[Single] +$Luminosity, +# Set the Spread of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Spread')] +[Single] +$Spread, +# Set the Speed of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Speed')] +[Single] +$Speed, +# Set the Alpha_Percentage of OBSRainbowShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Vertical of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the Rotational of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Rotational')] +[Management.Automation.SwitchParameter] +$Rotational, +# Set the Rotation_Offset of OBSRainbowShader +[Alias('Rotation_Offset')] +[ComponentModel.DefaultBindingProperty('Rotation_Offset')] +[Single] +$RotationOffset, +# Set the Apply_To_Image of OBSRainbowShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRainbowShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRainbowShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRainbowShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Notes of OBSRainbowShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rainbow' +$ShaderNoun = 'OBSRainbowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Rainbow shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 13, 2022 +uniform float Saturation< + string label = "Saturation"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.8; // +uniform float Luminosity< + string label = "Luminosity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; // +uniform float Spread< + string label = "Spread"; + string widget_type = "slider"; + float minimum = 0.5; + float maximum = 10.0; + float step = 0.01; +> = 3.8; // +uniform float Speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 2.4; // +uniform float Alpha_Percentage< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; // +uniform bool Vertical; +uniform bool Rotational; +uniform float Rotation_Offset< + string label = "Rotation Offset"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 6.28318531; + float step = 0.001; +> = 0.0; // +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string Notes< + string widget_type = "info"; +> = "Spread is wideness of color and is limited between .25 and 10. Edit at your own risk"; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float hueToRGB(float v1, float v2, float vH) { + vH = frac(vH); + if ((6.0 * vH) < 1.0) return (v1 + (v2 - v1) * 6.0 * vH); + if ((2.0 * vH) < 1.0) return (v2); + if ((3.0 * vH) < 2.0) return (v1 + (v2 - v1) * ((0.6666666666666667) - vH) * 6.0); + return clamp(v1, 0.0, 1.0); } +float4 HSLtoRGB(float4 hsl) { + float4 rgb = float4(0.0, 0.0, 0.0, hsl.w); + float v1 = 0.0; + float v2 = 0.0; + + if (hsl.y == 0) { + rgb.xyz = hsl.zzz; + } + else { + + if (hsl.z < 0.5) { + v2 = hsl.z * (1 + hsl.y); + } + else { + v2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + } + + v1 = 2.0 * hsl.z - v2; + + rgb.x = hueToRGB(v1, v2, hsl.x + (0.3333333333333333)); + rgb.y = hueToRGB(v1, v2, hsl.x); + rgb.z = hueToRGB(v1, v2, hsl.x - (0.3333333333333333)); + + } + + return rgb; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemId { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemId')] -[Alias('obs.powershell.websocket.GetSceneItemId')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, +float4 mainImage(VertData v_in) : TARGET +{ + float2 lPos = (v_in.uv * uv_scale + uv_offset)/ clamp(Spread, 0.25, 10.0); + float time = (elapsed_time * clamp(Speed, -5.0, 5.0)) / clamp(Spread, 0.25, 10.0); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('searchOffset')] -[ValidateRange(-1,[int]::MaxValue)] -[double] -$SearchOffset, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + //set colors and direction + float hue = (-1 * lPos.x) / 2.0; + if (Rotational && (Vertical == false)) + { + float timeWithOffset = time + Rotation_Offset; + float sine = sin(timeWithOffset); + float cosine = cos(timeWithOffset); + hue = (lPos.x * cosine + lPos.y * sine) * 0.5; + } -process { + if (Vertical && (Rotational == false)) + { + hue = (-1 * lPos.y) * 0.5; + } + hue += time; + hue = frac(hue); + float4 hsl = float4(hue, clamp(Saturation, 0.0, 1.0), clamp(Luminosity, 0.0, 1.0), 1.0); + float4 rgba = HSLtoRGB(hsl); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = 0.30*color.r+0.59*color.g+0.11*color.b+1.0*color.a; + float4 luma_color = float4(luma, luma, luma, luma); + if (Replace_Image_Color) + color = luma_color; + rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54595,355 +49425,396 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemIndex { - +function Get-OBSRainWindowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemIndex')] -[Alias('obs.powershell.websocket.GetSceneItemIndex')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRainWindowShader','Add-OBSRainWindowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the size of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('size')] +[Single] +$Size, +# Set the blurSize of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('blurSize')] +[Single] +$BlurSize, +# Set the trail_strength of OBSRainWindowShader +[Alias('trail_strength')] +[ComponentModel.DefaultBindingProperty('trail_strength')] +[Single] +$TrailStrength, +# Set the trail_color of OBSRainWindowShader +[Alias('trail_color')] +[ComponentModel.DefaultBindingProperty('trail_color')] +[Single] +$TrailColor, +# Set the speed of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the debug of OBSRainWindowShader +[ComponentModel.DefaultBindingProperty('debug')] +[Management.Automation.SwitchParameter] +$DebugShader, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rain-window' +$ShaderNoun = 'OBSRainWindowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/slfSzS adopted for OBS by Exeldro +// shader derived from Heartfelt - by Martijn Steinrucken aka BigWings - 2017 +// https://www.shadertoy.com/view/ltffzl +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +uniform float size< + string label = "Rain Drop Size"; + string widget_type = "slider"; + float minimum = 0.001; + float maximum = 0.5; + float step = 0.01; +> = 0.2; +uniform float blurSize< + string label = "Blur Radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 32.0; // BLUR SIZE (Radius) +uniform float trail_strength< + string label = "Trail Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform float trail_color< + string label = "Trail Color"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 40.0; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.01; +> = 100.0; +uniform bool debug = false; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float fract(float v){ + return v - floor(v); } +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemLocked { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemLocked')] -[Alias('obs.powershell.websocket.GetSceneItemLocked')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - +float3 fract3(float3 v){ + return float3(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z)); +} -process { +float3 fract4(float4 v){ + return float4(v.x - floor(v.x), v.y - floor(v.y), v.z - floor(v.z), v.w - floor(v.w)); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float3 N13(float p) { + // from DAVE HOSKINS + float3 p3 = fract3(float3(p, p, p) * float3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yzx + 19.19); + return fract3(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 N14(float t) { + return fract4(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.)); +} +float N(float t) { + return fract(sin(t*12345.564)*7658.76); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float Saw(float b, float t) { + return smoothstep(0., b, t)*smoothstep(1., b, t); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" +float2 Drops(float2 uv, float t) { + + float2 UV = uv; + + // DEFINE GRID + uv.y += t*0.8; + float2 a = float2(6., 1.); + float2 grid = a*2.; + float2 id = floor(uv*grid); + + // RANDOM SHIFT Y + float colShift = N(id.x); + uv.y += colShift; + + // DEFINE SPACES + id = floor(uv*grid); + float3 n = N13(id.x*35.2+id.y*2376.1); + float2 st = fract2(uv*grid)-float2(.5, 0); + + // POSITION DROPS + //clamp(2*x,0,2)+clamp(1-x*.5, -1.5, .5)+1.5-2 + float x = n.x-.5; + + float y = UV.y*20.; + + float distort = sin(y+sin(y)); + x += distort*(.5-abs(x))*(n.z-.5); + x *= .7; + float ti = fract(t+n.z); + y = (Saw(.85, ti)-.5)*.9+.5; + float2 p = float2(x, y); - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + // DROPS + float d = length((st-p)*a.yx); + + float dSize = size; + + float Drop = smoothstep(dSize, .0, d); + + + float r = sqrt(smoothstep(1., y, st.y)); + float cd = abs(st.x-x); + + // TRAILS + float trail = smoothstep((dSize*.5+.03)*r, (dSize*.5-.05)*r, cd); + float trailFront = smoothstep(-.02, .02, st.y-y); + trail *= trailFront; + + + // DROPLETS + y = UV.y; + y += N(id.x); + float trail2 = smoothstep(dSize*r, .0, cd); + float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; + y = fract(y*10.)+(st.y-.5); + float dd = length(st-float2(x, y)); + droplets = smoothstep(dSize*N(id.x), 0., dd); + float m = Drop+droplets*r*trailFront; + if(debug){ + m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; //DEBUG SPACES + } + + + return float2(m, trail); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float StaticDrops(float2 uv, float t) { + uv *= 30.; + + float2 id = floor(uv); + uv = fract2(uv)-.5; + float3 n = N13(id.x*107.45+id.y*3543.654); + float2 p = (n.xy-.5)*0.5; + float d = length(uv-p); + + float fade = Saw(.025, fract(t+n.z)); + float c = smoothstep(size, 0., d)*fract(n.z*10.)*fade; + return c; } +float2 Rain(float2 uv, float t) { + //float s = StaticDrops(uv, t); + float2 r1 = Drops(uv, t); + float2 r2 = Drops(uv*1.8, t); + float c; + if(debug){ + c = r1.x; + }else{ + c = r1.x+r2.x;//s+r1.x+r2.x; + } + + c = smoothstep(.3, 1., c); + + if(debug){ + return float2(c, r1.y); + }else{ + return float2(c, max(r1.y, r2.y)); + } +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemSource { +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv;//(fragCoord.xy-.5*iResolution.xy) / iResolution.y; + uv.y = 1.0 - uv.y; + uv = uv * uv_scale; + float2 UV = v_in.uv; + float T = elapsed_time * speed / 100.0; + + float t = T*.2; + + UV = (UV-.5)*(.9)+.5; + float2 c = Rain(uv, t); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemSource')] -[Alias('obs.powershell.websocket.GetSceneItemSource')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + float2 e = float2(.001, 0.); //pixel offset + float cx = Rain(uv+e, t).x; + float cy = Rain(uv+e.yx, t).x; + float2 n = float2(cx-c.x, cy-c.x); //normals -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + // BLUR derived from existical https://www.shadertoy.com/view/Xltfzj + float Pi = 6.28318530718; // Pi*2 -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + // GAUSSIAN BLUR SETTINGS {{{ + float Directions = 32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = 8.0; // BLUR QUALITY (Default 4.0 - More is better but slower) + // GAUSSIAN BLUR SETTINGS }}} + float2 Radius = blurSize/uv_size; + float3 col = image.Sample(textureSampler, UV).rgb; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if(blurSize > 0.0){ + // Blur calculations + for(float d=0.0; dAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -54952,117 +49823,199 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneItemTransform { - +function Get-OBSRectangularDropShadowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneItemTransform')] -[Alias('obs.powershell.websocket.GetSceneItemTransform')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRectangularDropShadowShader','Add-OBSRectangularDropShadowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the shadow_offset_x of OBSRectangularDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSRectangularDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSRectangularDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the shadow_color of OBSRectangularDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] +[String] +$ShadowColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rectangular_drop_shadow' +$ShaderNoun = 'OBSRectangularDropShadowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Exeldro February 22, 2022 +uniform int shadow_offset_x< + string label = "shadow offset x"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +>; +uniform int shadow_offset_y< + string label = "shadow offset y"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 100; + int step = 1; +> = 1; +uniform float4 shadow_color; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_samples = int(pow(shadow_blur_size * 2 + 1, 2)); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float start_of_overlap_x = max(0, shadow_uv.x - shadow_blur_size * uv_pixel_interval.x); + float end_of_overlap_x = min(1, shadow_uv.x + shadow_blur_size * uv_pixel_interval.x); + float x_proportion = (end_of_overlap_x - start_of_overlap_x) / (2 * shadow_blur_size * uv_pixel_interval.x); + + float start_of_overlap_y = max(0, shadow_uv.y - shadow_blur_size * uv_pixel_interval.y); + float end_of_overlap_y = min(1, shadow_uv.y + shadow_blur_size * uv_pixel_interval.y); + float y_proportion = (end_of_overlap_y - start_of_overlap_y) / (2 * shadow_blur_size * uv_pixel_interval.y); + + float4 final_shadow_color = float4(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a * x_proportion * y_proportion); + + return final_shadow_color * (1-color.a) + color; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55071,111 +50024,204 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneSceneTransitionOverride { - +function Get-OBSReflectShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.GetSceneSceneTransitionOverride')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSReflectShader','Add-OBSReflectShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Horizontal of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Horizontal')] +[Management.Automation.SwitchParameter] +$Horizontal, +# Set the Vertical of OBSReflectShader +[ComponentModel.DefaultBindingProperty('Vertical')] +[Management.Automation.SwitchParameter] +$Vertical, +# Set the center_x_percent of OBSReflectShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSReflectShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Reflect' +$ShaderNoun = 'OBSReflectShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Reflect Shader +// Reflects horizontally and/or vertically. - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool Horizontal< + string label = "Reflect horizontally"; +> = false; +uniform bool Vertical< + string label = "Reflect vertically"; +> = true; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform int center_x_percent< + string label = "center x percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + + if (Horizontal == true) { + if (pos.x < center_pos.x) { + pos.x = center_pos.x - pos.x; + } else if (pos.x == center_pos.x) { + pos.x = pos.x; + } else { + pos.x = pos.x - center_pos.x; + } + } + if (Vertical == true) { + if (pos.y < center_pos.y) { + pos.y = center_pos.y - pos.y; + } else if (pos.y == center_pos.y) { + pos.y = pos.y; + } else { + pos.y = pos.y - center_pos.y; + } + } + + return image.Sample(textureSampler, pos); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55184,101 +50230,169 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSceneTransition { - +function Get-OBSRemovePartialPixelsShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSceneTransitionList')] -[Alias('obs.powershell.websocket.GetSceneTransitionList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRemovePartialPixelsShader','Add-OBSRemovePartialPixelsShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the minimum_alpha_percent of OBSRemovePartialPixelsShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the notes of OBSRemovePartialPixelsShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'remove_partial_pixels' +$ShaderNoun = 'OBSRemovePartialPixelsShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Remove Partial Pixels shader by Charles Fettinger for obs-shaderfilter plugin 8/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGL by Exeldro February 21, 2022 +uniform int minimum_alpha_percent< + string label = "minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "Removes partial pixels, excellent for cleaning greenscreen. Default Minimum Alpha Percent is 50%, lowering will reveal more pixels"; +float4 mainImage(VertData v_in) : TARGET +{ + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + else + { + return float4(output_color); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55287,111 +50401,204 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceActive { - +function Get-OBSRepeatGridCenterCropShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceActive')] -[Alias('obs.powershell.websocket.GetSourceActive')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatGridCenterCropShader','Add-OBSRepeatGridCenterCropShader')] param( - +# Set the ViewProj of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the alpha of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('alpha')] +[Single] +$Alpha, +# Set the columns of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('columns')] +[Single] +$Columns, +# Set the rows of OBSRepeatGridCenterCropShader +[ComponentModel.DefaultBindingProperty('rows')] +[Single] +$Rows, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat_grid_center_crop' +$ShaderNoun = 'OBSRepeatGridCenterCropShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// repeat_grid_center_crop.effect +uniform float4x4 ViewProj; +uniform texture2d image; +sampler_state def_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float alpha = 1.0; +uniform float columns = 3.0; +uniform float rows = 1.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +VertInOut VSDefault(VertInOut vert_in) { + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1), ViewProj); + vert_out.uv = vert_in.uv * float2(columns, rows); + return vert_out; +} + +float4 PSMain(VertInOut vert_in) : TARGET { + // Calculate fractional UV within grid cell + float2 cell_uv = frac(vert_in.uv); + + // Calculate center crop parameters + float horizontalCropStart = 0.5 - 0.5/columns; + + // Map to centered portion of original image + float2 original_uv = float2( + cell_uv.x / columns + horizontalCropStart, + cell_uv.y / rows + ); + + // Sample the image + float4 col = image.Sample(def_sampler, original_uv); + col.a *= alpha; + return col; +} + +technique Draw { + pass { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSMain(vert_in); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55400,224 +50607,267 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilter { - +function Get-OBSRepeatShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilter')] -[Alias('obs.powershell.websocket.GetSourceFilter')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatShader','Add-OBSRepeatShader')] param( - +# Set the ViewProj of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the color_matrix of OBSRepeatShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color_range_min of OBSRepeatShader +[Alias('color_range_min')] +[ComponentModel.DefaultBindingProperty('color_range_min')] +[Single[]] +$ColorRangeMin, +# Set the color_range_max of OBSRepeatShader +[Alias('color_range_max')] +[ComponentModel.DefaultBindingProperty('color_range_max')] +[Single[]] +$ColorRangeMax, +# Set the image of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSRepeatShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRepeatShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRepeatShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRepeatShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSRepeatShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSRepeatShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the alpha of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('alpha')] +[Single] +$Alpha, +# Set the copies of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('copies')] +[Single] +$Copies, +# Set the notes of OBSRepeatShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat' +$ShaderNoun = 'OBSRepeatShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +uniform float4x4 ViewProj; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; +uniform texture2d image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterDefaultSettings { +uniform float alpha< + string label = "Alpha"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 3.0; + float step = 0.001; +> = 1.0; +uniform float copies< + string label = "Copies"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; +uniform string notes< + string widget_type = "info"; +> = ''copies, use a number that has a square root. Alpha adjusts the alpha level of the copies (recommend 0.5-2.0 recommend)''; +sampler_state def_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterDefaultSettings')] -[Alias('obs.powershell.websocket.GetSourceFilterDefaultSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterKind')] -[string] -$FilterKind, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +VertInOut VSDefault(VertInOut vert_in) +{ + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv = vert_in.uv * sqrt(copies); + return vert_out; +} +float4 PSDrawBare(VertInOut vert_in) : TARGET +{ + float4 rgba = image.Sample(def_sampler, vert_in.uv); + rgba.a *= alpha; + return rgba; +} -process { +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55626,214 +50876,300 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterKind { - +function Get-OBSRepeatTextureShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterKindList')] -[Alias('obs.powershell.websocket.GetSourceFilterKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRepeatTextureShader','Add-OBSRepeatTextureShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the color_matrix of OBSRepeatTextureShader +[Alias('color_matrix')] +[ComponentModel.DefaultBindingProperty('color_matrix')] +[Single[][]] +$ColorMatrix, +# Set the color_range_min of OBSRepeatTextureShader +[Alias('color_range_min')] +[ComponentModel.DefaultBindingProperty('color_range_min')] +[Single[]] +$ColorRangeMin, +# Set the color_range_max of OBSRepeatTextureShader +[Alias('color_range_max')] +[ComponentModel.DefaultBindingProperty('color_range_max')] +[Single[]] +$ColorRangeMax, +# Set the image of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the tex_image of OBSRepeatTextureShader +[Alias('tex_image')] +[ComponentModel.DefaultBindingProperty('tex_image')] +[String] +$TexImage, +# Set the elapsed_time of OBSRepeatTextureShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRepeatTextureShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRepeatTextureShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRepeatTextureShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the uv_size of OBSRepeatTextureShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the rand_f of OBSRepeatTextureShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the blend of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('blend')] +[Single] +$Blend, +# Set the copies of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('copies')] +[Single] +$Copies, +# Set the notes of OBSRepeatTextureShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the alpha_percentage of OBSRepeatTextureShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Single] +$AlphaPercentage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'repeat_texture' +$ShaderNoun = 'OBSRepeatTextureShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Repeat Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +uniform float4x4 ViewProj; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; +uniform texture2d image; +uniform texture2d tex_image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float2 uv_size; +uniform float rand_f; -} +uniform float blend< + string label = "Blend"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 3.0; + float step = 0.001; +> = 1.0; +uniform float copies< + string label = "Copies"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 4.0; +uniform string notes< + string widget_type = "info"; +> = ''copies, use a number that has a square root. Blend adjusts the ratio of source and texture''; +uniform float alpha_percentage< + string label = "alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100.0; - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceFilterList { +sampler_state tex_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; +sampler_state base_sampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceFilterList')] -[Alias('obs.powershell.websocket.GetSourceFilterList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( +struct VertIn { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, +struct VertOut { + float4 pos : POSITION; + float2 uv_0 : TEXCOORD0; + float2 uv_1 : TEXCOORD1; +}; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +VertOut VSDefault(VertIn vert_in) +{ + VertOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1 ), ViewProj); + vert_out.uv_1 = vert_in.uv_0; + vert_out.uv_0 = vert_in.uv_0 * sqrt(copies); + return vert_out; +} +float4 PSDrawBare(VertOut vert_in) : TARGET +{ + float alpha = clamp(alpha_percentage * 0.01 ,-1.0,2.0); + float4 tex = tex_image.Sample(tex_sampler, vert_in.uv_0); + float4 base = image.Sample(base_sampler, vert_in.uv_1); -process { + return (1 - alpha) * base + (alpha) * tex; +} +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55842,134 +51178,199 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSourceScreenshot { - +function Get-OBSRGBAPercentShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSourceScreenshot')] -[Alias('obs.powershell.websocket.GetSourceScreenshot')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRGBAPercentShader','Add-OBSRGBAPercentShader')] param( - +# Set the RedPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('RedPercent')] +[Single] +$RedPercent, +# Set the GreenPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('GreenPercent')] +[Single] +$GreenPercent, +# Set the BluePercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('BluePercent')] +[Single] +$BluePercent, +# Set the AlphaPercent of OBSRGBAPercentShader +[ComponentModel.DefaultBindingProperty('AlphaPercent')] +[Single] +$AlphaPercent, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, +process { +$shaderName = 'RGBA_Percent' +$ShaderNoun = 'OBSRGBAPercentShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple RGBA Percent Shader +// Allows Red, Green, or Blue to be adjusted between 0-200% +// Allows Alpha to be adjusted between 0/100% +uniform float RedPercent< + string label = "Red percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, +uniform float GreenPercent< + string label = "Green percentage"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 200; + float step = 1.0; +> = 100; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform float BluePercent< + string label = "Blue percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200; + float step = 1.0; +> = 100.0; -process { +uniform float AlphaPercent< + string label = "Alpha percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 pos = v_in.uv; + + float4 imageColors = image.Sample(textureSampler, v_in.uv); + float4 adjustedColor = float4( + imageColors.r * (RedPercent * 0.01), + imageColors.g * (GreenPercent * 0.01), + imageColors.b * (BluePercent * 0.01), + imageColors.a * (AlphaPercent * 0.01) + ); + return adjustedColor; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -55978,204 +51379,268 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSSpecialInputs { - +function Get-OBSRgbColorWheelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetSpecialInputs')] -[Alias('obs.powershell.websocket.GetSpecialInputs')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbColorWheelShader','Add-OBSRgbColorWheelShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed of OBSRgbColorWheelShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the color_depth of OBSRgbColorWheelShader +[Alias('color_depth')] +[ComponentModel.DefaultBindingProperty('color_depth')] +[Single] +$ColorDepth, +# Set the Apply_To_Image of OBSRgbColorWheelShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSRgbColorWheelShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Apply_To_Specific_Color of OBSRgbColorWheelShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSRgbColorWheelShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Alpha_Percentage of OBSRgbColorWheelShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the center_width_percentage of OBSRgbColorWheelShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRgbColorWheelShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) -process { +process { +$shaderName = 'rgb_color_wheel' +$ShaderNoun = 'OBSRgbColorWheelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGB Color Wheel shader by Charles Fettinger for obs-shaderfilter plugin 5/2020 +// https://github.com/Oncorporation/obs-shaderfilter +//Converted to OpenGl by Q-mii & Exeldro February 25, 2022 +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 15.0; + float step = 0.1; +> = 2.0; +uniform float color_depth< + string label = "Color Depth"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.1; +> = 2.10; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform float Alpha_Percentage< + string label = "Alpha Percentage"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.1; +> = 100; // +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; + +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.14159265f;//acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + float2 st = v_in.uv* uv_scale; + float2 toCenter = center_pixel_coordinates - st ; + float r = length(toCenter) * color_depth; + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(speed ,18)) / 18; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + rgba.rgb = hsv2rgb(float3((angle / PI*0.5) + angleMod,r,1.0)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = color.r * 0.299 + color.g * 0.587 + color.b * 0.114; + if (Replace_Image_Color) + color = float4(luma, luma, luma, luma); + rgba = lerp(original_color, rgba * color,clamp(Alpha_Percentage *.01 ,0,1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSStats { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStats')] -[Alias('obs.powershell.websocket.GetStats')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56184,101 +51649,212 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamServiceSettings { - +function Get-OBSRgbSplitShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamServiceSettings')] -[Alias('obs.powershell.websocket.GetStreamServiceSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbSplitShader','Add-OBSRgbSplitShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the redx of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('redx')] +[Single] +$Redx, +# Set the redy of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('redy')] +[Single] +$Redy, +# Set the greenx of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('greenx')] +[Single] +$Greenx, +# Set the greeny of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('greeny')] +[Single] +$Greeny, +# Set the bluex of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('bluex')] +[Single] +$Bluex, +# Set the bluey of OBSRgbSplitShader +[ComponentModel.DefaultBindingProperty('bluey')] +[Single] +$Bluey, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rgb_split' +$ShaderNoun = 'OBSRgbSplitShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float redx< + string label = "Red X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 2.00; +uniform float redy< + string label = "Red Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greenx< + string label = "Green X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float greeny< + string label = "Green Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; +uniform float bluex< + string label = "Blue X"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = -2.00; +uniform float bluey< + string label = "Blue Y"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.01; +> = 0.00; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + if(redx != 0.0 || redy != 0.0) + c.r = image.Sample(textureSampler, v_in.uv + float2(redx/100.0, redy/100.0)).r; + if(greenx != 0.0 || greeny != 0.0) + c.g = image.Sample(textureSampler, v_in.uv + float2(greenx/100.0, greeny/100.0)).g; + if(bluex != 0.0 || bluey != 0.0) + c.b = image.Sample(textureSampler, v_in.uv + float2(bluex/100.0, bluey/100.0)).b; + return c; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56287,101 +51863,225 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStreamStatus { - +function Get-OBSRgbvisibilityShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStreamStatus')] -[Alias('obs.powershell.websocket.GetStreamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRgbvisibilityShader','Add-OBSRgbvisibilityShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the Red of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Red')] +[Single] +$Red, +# Set the Green of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Green')] +[Single] +$Green, +# Set the Blue of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('Blue')] +[Single] +$Blue, +# Set the RedVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('RedVisibility')] +[Single] +$RedVisibility, +# Set the GreenVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('GreenVisibility')] +[Single] +$GreenVisibility, +# Set the BlueVisibility of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('BlueVisibility')] +[Single] +$BlueVisibility, +# Set the notes of OBSRgbvisibilityShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rgbvisibility' +$ShaderNoun = 'OBSRgbvisibilityShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGB visibility separation filter, created by EposVox +uniform float Red< + string label = "Red"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Green< + string label = "Green"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float Blue< + string label = "Blue"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 10.0; + float step = 0.01; +> = 2.2; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +uniform float RedVisibility< + string label = "Red Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float GreenVisibility< + string label = "Green Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform float BlueVisibility< + string label = "Blue Visibility"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; + +uniform string notes< + string widget_type = "info"; +> = "Modify Colors to correct for gamma, use equal values for general correction."; + +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float redChannel = pow(c.r, Red) * RedVisibility; + float greenChannel = pow(c.g, Green) * GreenVisibility; + float blueChannel = pow(c.b, Blue) * BlueVisibility; + + return float4(redChannel, greenChannel, blueChannel, c.a); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56390,204 +52090,291 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSStudioModeEnabled { - +function Get-OBSRGSSAAShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetStudioModeEnabled')] -[Alias('obs.powershell.websocket.GetStudioModeEnabled')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRGSSAAShader','Add-OBSRGSSAAShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ColorSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('ColorSigma')] +[Single] +$ColorSigma, +# Set the SpatialSigma of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('SpatialSigma')] +[Single] +$SpatialSigma, +# Set the notes of OBSRGSSAAShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'RGSSAA' +$ShaderNoun = 'OBSRGSSAAShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// RGSSAA shader by Eliseu Amaro for obs-shaderfilter plugin 2/2024 +// https://github.com/exeldro/obs-shaderfilter/tree/master +// Using edge detection shader as a base, created by Hallatore +// https://forums.unrealengine.com/t/sharper-image-without-the-edge-artifacts/108461 +uniform float ColorSigma< + string label = "Color Sigma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float SpatialSigma< + string label = "Spatial Sigma"; + string widget_type = "slider"; + float minimum = 0.1; + float maximum = 1.0; + float step = 0.1; +> = 1.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform string notes< + string widget_type = "info"; +> = "Performs RGSSAA, a form of anti-aliasing. Implementation roughly follows the original with color and spatial sigma (or strengths) parameters. Useful to apply before a sharpen pass (e.g on a webcam feed)." - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float Luminance(float3 rgb) +{ + return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float4 mainImage(VertData v_in) : TARGET +{ + float3 SceneColor = image.Sample(textureSampler, v_in.uv).rgb; + float2 SceneUV = v_in.uv; + float2 TexelScale = 1.0f/uv_size; -} + const float SQRT2 = 1.4142135624; + const float PI = 3.141592654; + const float angle = PI / 8.0; + const float cs = cos(angle); + const float sn = sin(angle); + // Rotated grid samples + float3 C1 = + image.Sample(textureSampler, SceneUV + float2(cs, -sn) * TexelScale).rgb; + float3 C2 = + image.Sample(textureSampler, SceneUV + float2(-cs, -sn) * TexelScale).rgb; + float3 C3 = + image.Sample(textureSampler, SceneUV + float2(-sn, cs) * TexelScale).rgb; + float3 C4 = + image.Sample(textureSampler, SceneUV + float2(sn, cs) * TexelScale).rgb; + float3 C5 = + image.Sample(textureSampler, SceneUV + float2(cs * SQRT2, 0) * TexelScale).rgb; + float3 C6 = + image.Sample(textureSampler, SceneUV + float2(0, sn *SQRT2) * TexelScale).rgb; + float3 C7 = + image.Sample(textureSampler, SceneUV + float2(-cs * SQRT2, 0) * TexelScale).rgb; + float3 C8 = + image.Sample(textureSampler, SceneUV + float2(0, -sn *SQRT2) * TexelScale).rgb; -} + // Luminance edge detection + float A0 = Luminance(SceneColor); + float CL1 = Luminance(C1); + float L1 = ((max(CL1, A0)) / (min(CL1, A0) + 0.001) - 1); + float CL2 = Luminance(C2); + float L2 = ((max(CL2, A0)) / (min(CL2, A0) + 0.001) - 1); + float CL3 = Luminance(C3); + float L3 = ((max(CL3, A0)) / (min(CL3, A0) + 0.001) - 1); + float CL4 = Luminance(C4); + float L4 = ((max(CL4, A0)) / (min(CL4, A0) + 0.001) - 1); + float CL5 = Luminance(C5); + float L5 = ((max(CL5, A0)) / (min(CL5, A0) + 0.001) - 1); + float CL6 = Luminance(C6); + float L6 = ((max(CL6, A0)) / (min(CL6, A0) + 0.001) - 1); + float CL7 = Luminance(C7); + float L7 = ((max(CL7, A0)) / (min(CL7, A0) + 0.001) - 1); + float CL8 = Luminance(C8); + float L8 = ((max(CL8, A0)) / (min(CL8, A0) + 0.001) - 1); + float NeighborDifference = max(max(max(L1, L2), max(L3, L4)), max(max(L5, L6), max(L7, L8))); - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSTransitionKind { + // Calculate distance-based weights + float2 Dist1 = float2(cs, -sn); + float2 Dist2 = float2(-cs, -sn); + float2 Dist3 = float2(-sn, cs); + float2 Dist4 = float2(sn, cs); + float2 Dist5 = float2(cs * SQRT2, 0); + float2 Dist6 = float2(0, sn * SQRT2); + float2 Dist7 = float2(-cs * SQRT2, 0); + float2 Dist8 = float2(0, -sn * SQRT2); + float SW1 = exp(-dot(Dist1, Dist1) / (2.0 * SpatialSigma * SpatialSigma)); + float SW2 = exp(-dot(Dist2, Dist2) / (2.0 * SpatialSigma * SpatialSigma)); + float SW3 = exp(-dot(Dist3, Dist3) / (2.0 * SpatialSigma * SpatialSigma)); + float SW4 = exp(-dot(Dist4, Dist4) / (2.0 * SpatialSigma * SpatialSigma)); + float SW5 = exp(-dot(Dist5, Dist5) / (2.0 * SpatialSigma * SpatialSigma)); + float SW6 = exp(-dot(Dist6, Dist6) / (2.0 * SpatialSigma * SpatialSigma)); + float SW7 = exp(-dot(Dist7, Dist7) / (2.0 * SpatialSigma * SpatialSigma)); + float SW8 = exp(-dot(Dist8, Dist8) / (2.0 * SpatialSigma * SpatialSigma)); + // Color weights + float3 ColorDiff1 = SceneColor.rgb - C1; + float3 ColorDiff2 = SceneColor.rgb - C2; + float3 ColorDiff3 = SceneColor.rgb - C3; + float3 ColorDiff4 = SceneColor.rgb - C4; + float3 ColorDiff5 = SceneColor.rgb - C5; + float3 ColorDiff6 = SceneColor.rgb - C6; + float3 ColorDiff7 = SceneColor.rgb - C7; + float3 ColorDiff8 = SceneColor.rgb - C8; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetTransitionKindList')] -[Alias('obs.powershell.websocket.GetTransitionKindList')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float CW1 = exp(-dot(ColorDiff1, ColorDiff1) / (2.0 * ColorSigma * ColorSigma)); + float CW2 = exp(-dot(ColorDiff2, ColorDiff2) / (2.0 * ColorSigma * ColorSigma)); + float CW3 = exp(-dot(ColorDiff3, ColorDiff3) / (2.0 * ColorSigma * ColorSigma)); + float CW4 = exp(-dot(ColorDiff4, ColorDiff4) / (2.0 * ColorSigma * ColorSigma)); + float CW5 = exp(-dot(ColorDiff5, ColorDiff5) / (2.0 * ColorSigma * ColorSigma)); + float CW6 = exp(-dot(ColorDiff6, ColorDiff6) / (2.0 * ColorSigma * ColorSigma)); + float CW7 = exp(-dot(ColorDiff7, ColorDiff7) / (2.0 * ColorSigma * ColorSigma)); + float CW8 = exp(-dot(ColorDiff8, ColorDiff8) / (2.0 * ColorSigma * ColorSigma)); + // Mixing weights + float W1 = SW1 * CW1; + float W2 = SW2 * CW2; + float W3 = SW3 * CW3; + float W4 = SW4 * CW4; + float W5 = SW5 * CW5; + float W6 = SW6 * CW6; + float W7 = SW7 * CW7; + float W8 = SW8 * CW8; + float TotalWeight = W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; -process { + // Weighted color + float3 AAResult = (C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4 + C5 * W5 + C6 * W6 + + C7 * W7 + C8 * W8) / + max(TotalWeight, 0.0001); + // Blend it + float4 LuminanceNeightbors = float4(CL1, CL2, CL3, CL4); + float4 LuminanceNeightbors2 = float4(CL5, CL6, CL7, CL8); + float4 A0LuminanceNeightbors = abs(A0 - LuminanceNeightbors); + float4 A0LuminanceNeightbors2 = abs(A0 - LuminanceNeightbors2); + float A0Max = max(max(A0LuminanceNeightbors.r, A0LuminanceNeightbors.g), max(A0LuminanceNeightbors.b, A0LuminanceNeightbors.a)); + float A0Max2 = max(max(A0LuminanceNeightbors2.r, A0LuminanceNeightbors2.g), max(A0LuminanceNeightbors2.b, A0LuminanceNeightbors2.a)); + float HDREdge = max(A0Max, A0Max2); + float EdgeMask = saturate(1.0f - HDREdge); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return float4(lerp(SceneColor.rgb, AAResult, EdgeMask), 1.0); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56596,101 +52383,202 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVersion { - +function Get-OBSRippleShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVersion')] -[Alias('obs.powershell.websocket.GetVersion')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRippleShader','Add-OBSRippleShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the distance_factor of OBSRippleShader +[Alias('distance_factor')] +[ComponentModel.DefaultBindingProperty('distance_factor')] +[Single] +$DistanceFactor, +# Set the time_factor of OBSRippleShader +[Alias('time_factor')] +[ComponentModel.DefaultBindingProperty('time_factor')] +[Single] +$TimeFactor, +# Set the power_factor of OBSRippleShader +[Alias('power_factor')] +[ComponentModel.DefaultBindingProperty('power_factor')] +[Single] +$PowerFactor, +# Set the center_pos_x of OBSRippleShader +[Alias('center_pos_x')] +[ComponentModel.DefaultBindingProperty('center_pos_x')] +[Single] +$CenterPosX, +# Set the center_pos_y of OBSRippleShader +[Alias('center_pos_y')] +[ComponentModel.DefaultBindingProperty('center_pos_y')] +[Single] +$CenterPosY, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'ripple' +$ShaderNoun = 'OBSRippleShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform float distance_factor< + string label = "distance factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 12.0; +uniform float time_factor< + string label = "time factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform float power_factor< + string label = "power factor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 3.0; +uniform float center_pos_x< + string label = "center pos x"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float center_pos_y< + string label = "center pos y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 cPos = (v_in.uv * 2 ) -1; + float2 center_pos = float2(center_pos_x, center_pos_y); + float cLength = distance(cPos, center_pos); + float2 uv = v_in.uv+(cPos/cLength)*cos(cLength*distance_factor-elapsed_time*time_factor) * power_factor / 100.0; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56699,101 +52587,237 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVideoSettings { - +function Get-OBSRotatingSourceShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVideoSettings')] -[Alias('obs.powershell.websocket.GetVideoSettings')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRotatingSourceShader','Add-OBSRotatingSourceShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the spin_speed of OBSRotatingSourceShader +[Alias('spin_speed')] +[ComponentModel.DefaultBindingProperty('spin_speed')] +[Single] +$SpinSpeed, +# Set the rotation of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('rotation')] +[Single] +$Rotation, +# Set the zoomin of OBSRotatingSourceShader +[ComponentModel.DefaultBindingProperty('zoomin')] +[Single] +$Zoomin, +# Set the keep_aspectratio of OBSRotatingSourceShader +[Alias('keep_aspectratio')] +[ComponentModel.DefaultBindingProperty('keep_aspectratio')] +[Management.Automation.SwitchParameter] +$KeepAspectratio, +# Set the x_center of OBSRotatingSourceShader +[Alias('x_center')] +[ComponentModel.DefaultBindingProperty('x_center')] +[Single] +$XCenter, +# Set the y_center of OBSRotatingSourceShader +[Alias('y_center')] +[ComponentModel.DefaultBindingProperty('y_center')] +[Single] +$YCenter, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rotating-source' +$ShaderNoun = 'OBSRotatingSourceShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//spin speed higher the slower +uniform float spin_speed< + string label = "Spin Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float rotation< + string label = "Rotation"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.0; + float step = 0.1; +> = 0.0; +uniform float zoomin< + string label = "Zoom"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform bool keep_aspectratio = true; +uniform float x_center< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform float y_center< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +//main fragment code +//from lioran to nutella with love +float4 mainImage(VertData v_in) : TARGET +{ + float x_aspectratio = keep_aspectratio ? uv_size.x : 1.0; + float y_aspectratio = keep_aspectratio ? uv_size.y : 1.0; + //get position on of the texture and focus on the middle + float i_rotation; + if (spin_speed == 0){ + //turn angle number into pi number + i_rotation = rotation/57.295779513; + }else{ + //use elapsed time for spinning if spin speed is not 0 + i_rotation = elapsed_time * spin_speed; + } + float2 i_point; + i_point.x = (v_in.uv.x * x_aspectratio) - (x_aspectratio * x_center); + i_point.y = (v_in.uv.y * y_aspectratio) - (y_aspectratio * y_center); + + //get the angle from center , returns pi number + float i_dir = atan(i_point.y/i_point.x); + if(i_point.x < 0.0){ + i_dir += 3.14159265359; + } + + //get the distance from the centers + float i_distance = sqrt(pow(i_point.x,2) + pow(i_point.y,2)); + //multiple distance by the zoomin value + i_distance *= zoomin; + + //shift the texture position based on angle and distance from the middle + i_point.x = ((x_aspectratio*x_center)+cos(i_dir-i_rotation)*i_distance)/x_aspectratio; + i_point.y = ((y_aspectratio*y_center)+sin(i_dir-i_rotation)*i_distance)/y_aspectratio; + + //draw normally from new point + return image.Sample(textureSampler, i_point); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -56802,213 +52826,374 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Get-OBSVirtualCamStatus { - +function Get-OBSRotatoeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'GetVirtualCamStatus')] -[Alias('obs.powershell.websocket.GetVirtualCamStatus')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSRotatoeShader','Add-OBSRotatoeShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the ViewProj of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSRotatoeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSRotatoeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSRotatoeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSRotatoeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSRotatoeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSRotatoeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the speed_percent of OBSRotatoeShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the Axis_X of OBSRotatoeShader +[Alias('Axis_X')] +[ComponentModel.DefaultBindingProperty('Axis_X')] +[Single] +$AxisX, +# Set the Axis_Y of OBSRotatoeShader +[Alias('Axis_Y')] +[ComponentModel.DefaultBindingProperty('Axis_Y')] +[Single] +$AxisY, +# Set the Axis_Z of OBSRotatoeShader +[Alias('Axis_Z')] +[ComponentModel.DefaultBindingProperty('Axis_Z')] +[Single] +$AxisZ, +# Set the Angle_Degrees of OBSRotatoeShader +[Alias('Angle_Degrees')] +[ComponentModel.DefaultBindingProperty('Angle_Degrees')] +[Single] +$AngleDegrees, +# Set the Rotate_Transform of OBSRotatoeShader +[Alias('Rotate_Transform')] +[ComponentModel.DefaultBindingProperty('Rotate_Transform')] +[Management.Automation.SwitchParameter] +$RotateTransform, +# Set the Rotate_Pixels of OBSRotatoeShader +[Alias('Rotate_Pixels')] +[ComponentModel.DefaultBindingProperty('Rotate_Pixels')] +[Management.Automation.SwitchParameter] +$RotatePixels, +# Set the Rotate_Colors of OBSRotatoeShader +[Alias('Rotate_Colors')] +[ComponentModel.DefaultBindingProperty('Rotate_Colors')] +[Management.Automation.SwitchParameter] +$RotateColors, +# Set the center_width_percentage of OBSRotatoeShader +[Alias('center_width_percentage')] +[ComponentModel.DefaultBindingProperty('center_width_percentage')] +[Int32] +$CenterWidthPercentage, +# Set the center_height_percentage of OBSRotatoeShader +[Alias('center_height_percentage')] +[ComponentModel.DefaultBindingProperty('center_height_percentage')] +[Int32] +$CenterHeightPercentage, +# Set the notes of OBSRotatoeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rotatoe' +$ShaderNoun = 'OBSRotatoeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Rotation Effect By Charles Fettinger (https://github.com/Oncorporation) 10/2019 +//Converted to OpenGL by Q-mii, Exeldro, & skeletonbow +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform int speed_percent< + string label = "speed percentage"; + string widget_type = "slider"; + int minimum = -100; + int maximum = 100; + int step = 1; +> = 50; // +uniform float Axis_X< + string label = "Axis X"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.1; +> = 0.0; +uniform float Axis_Y< + string label = "Axis Y"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 0.0; +uniform float Axis_Z< + string label = "Axis Z"; + string widget_type = "slider"; + float minimum = -2.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; +uniform float Angle_Degrees< + string label = "Angle Degrees"; + string widget_type = "slider"; + float minimum = -180.0; + float maximum = 180.0; + float step = 0.01; +> = 45.0; +uniform bool Rotate_Transform = true; +uniform bool Rotate_Pixels = false; +uniform bool Rotate_Colors = false; +uniform int center_width_percentage< + string label = "center width percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height_percentage< + string label = "center height percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform string notes< + string widget_type = "info"; +> = " Choose axis, angle and speed, then rotate away! center_width_percentage & center_height_percentage allow you to change the pixel spin axis"; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float3x3 rotAxis(float3 axis, float a) { + float s=sin(a); + float c=cos(a); + float oc=1.0-c; + float3 as=axis*s; + + float3x3 p=float3x3(axis.x*axis,axis.y*axis,axis.z*axis); + float3x3 q=float3x3(c,-as.z,as.y,as.z,c,-as.x,-as.y,as.x,c); + return p*oc+q; } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); -} + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputFiltersDialog { + // use matrix to transform rotation + if (Rotate_Transform) + vert_out.pos.xyz = mul(vert_out.pos.xyz,rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; + vert_out.uv = v_in.uv * uv_scale + uv_offset; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputFiltersDialog')] -[Alias('obs.powershell.websocket.OpenInputFiltersDialog')] -param( + return vert_out; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +float4 mainImage(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float speed = speed_percent * 0.01; + // circular easing variable + float PI = 3.1415926535897932384626433832795; //acos(-1); + float PI180th = 0.0174532925; //PI divided by 180 + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = sin(elapsed_time * speed); + float angle_degrees = PI180th * Angle_Degrees; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // use matrix to transform pixels + if (Rotate_Pixels) + { + float2 center_pixel_coordinates = float2((center_width_percentage * 0.01), (center_height_percentage * 0.01) ); + rgba = image.Sample(textureSampler, mul(float3(v_in.uv - center_pixel_coordinates, 1.0), rotAxis(float3(Axis_X ,Axis_Y, Axis_Z ), (angle_degrees * t))).xy + center_pixel_coordinates); + } + if (Rotate_Colors) + rgba.rgb = mul(rgba.rgb, rotAxis(float3(Axis_X,Axis_Y,Axis_Z), (angle_degrees * t))).xyz; -process { + return rgba; +} +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57017,110 +53202,330 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputInteractDialog { - +function Get-OBSRoundedRect2Shader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputInteractDialog')] -[Alias('obs.powershell.websocket.OpenInputInteractDialog')] +[Alias('Set-OBSRoundedRect2Shader','Add-OBSRoundedRect2Shader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the corner_radius of OBSRoundedRect2Shader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedRect2Shader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRect2Shader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRect2Shader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRect2Shader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRect2Shader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# Set the faster_scan of OBSRoundedRect2Shader +[Alias('faster_scan')] +[ComponentModel.DefaultBindingProperty('faster_scan')] +[Management.Automation.SwitchParameter] +$FasterScan, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect2' +$ShaderNoun = 'OBSRoundedRect2Shader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "Border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 1.0; +uniform float border_alpha_end< + string label = "Border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float alpha_cut_off< + string label = "Aplha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.5; +uniform bool faster_scan = true; +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + float check_dist = float(corner_radius); + if(border_thickness > check_dist) + check_dist = border_thickness; + if(image.Sample(textureSampler, v_in.uv + float2(check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(-check_dist*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = int(-check_dist); + } + if(image.Sample(textureSampler, v_in.uv + float2(0,check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(check_dist); + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-check_dist*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = int(-check_dist); + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(!faster_scan || closedEdgeX != 0){ + [loop] for(int x = 1;float(x) check_dist && border_thickness > corner_radius){ + if(closedEdgeXabs < corner_radius && closedEdgeYabs < corner_radius){ + float cd = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(floor(cd) > corner_radius) + return float4(0.0,0.0,0.0,0.0); + if(cd > corner_radius){ + d = border_thickness + cd - corner_radius; + } else if(d > border_thickness){ + d = border_thickness; + } + }else if(d > border_thickness){ + d = border_thickness; + } + } + if(floor(d) <= check_dist){ + if(border_thickness > 0){ + if(ceil(check_dist-d) <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + ((check_dist-d)/ float(border_thickness))*(border_alpha_start-border_alpha_end); + if(border_alpha_start < border_alpha_end){ + fade_color.rgb = pixel.rgb * (1.0 - fade_color.a) + fade_color.rgb * fade_color.a; + fade_color.a = border_alpha_end + ((check_dist-d) / float(border_thickness))*(pixel.a-border_alpha_end); + } + if(d > check_dist) + fade_color.a *= 1.0 -(d - check_dist); + return fade_color; + }else if(d >= 0 && floor(check_dist-d) <= border_thickness && border_alpha_start >= border_alpha_end){ + float4 fade_color = border_color; + float f; + if(border_thickness > (check_dist-d)) + f = border_thickness - (check_dist-d); + else + f = 1.0 -((check_dist-d) - border_thickness); + fade_color.rgb = pixel.rgb * (1.0 - f) + fade_color.rgb * f; + return fade_color; + } + } + if (d > check_dist) + pixel.a *= 1.0 - (d - check_dist); + return pixel; + + } + return float4(0.0,0.0,0.0,0.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57129,232 +53534,354 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSInputPropertiesDialog { - +function Get-OBSRoundedRectPerCornerShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenInputPropertiesDialog')] -[Alias('obs.powershell.websocket.OpenInputPropertiesDialog')] +[Alias('Set-OBSRoundedRectPerCornerShader','Add-OBSRoundedRectPerCornerShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the corner_radius_tl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tl')] +[ComponentModel.DefaultBindingProperty('corner_radius_tl')] +[Int32] +$CornerRadiusTl, +# Set the corner_radius_tr of OBSRoundedRectPerCornerShader +[Alias('corner_radius_tr')] +[ComponentModel.DefaultBindingProperty('corner_radius_tr')] +[Int32] +$CornerRadiusTr, +# Set the corner_radius_br of OBSRoundedRectPerCornerShader +[Alias('corner_radius_br')] +[ComponentModel.DefaultBindingProperty('corner_radius_br')] +[Int32] +$CornerRadiusBr, +# Set the corner_radius_bl of OBSRoundedRectPerCornerShader +[Alias('corner_radius_bl')] +[ComponentModel.DefaultBindingProperty('corner_radius_bl')] +[Int32] +$CornerRadiusBl, +# Set the border_thickness of OBSRoundedRectPerCornerShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectPerCornerShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRectPerCornerShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRectPerCornerShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRectPerCornerShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect_per_corner' +$ShaderNoun = 'OBSRoundedRectPerCornerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int corner_radius_tl< + string label = "Corner radius top left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_tr< + string label = "Corner radius top right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_br< + string label = "Corner radius bottom right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_bl< + string label = "Corner radius bottom left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 mainImage(VertData v_in) : TARGET +{ + float4 pixel = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(pixel.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + int corner_radius_top = corner_radius_tl>corner_radius_tr?corner_radius_tl:corner_radius_tr; + int corner_radius_right = corner_radius_tr>corner_radius_br?corner_radius_tr:corner_radius_br; + int corner_radius_bottom = corner_radius_bl>corner_radius_br?corner_radius_bl:corner_radius_br; + int corner_radius_left = corner_radius_tl>corner_radius_bl?corner_radius_tl:corner_radius_bl; + + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return pixel; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x 0 && closedEdgeY < 0){ + corner_radius = corner_radius_tr; + }else if(closedEdgeX > 0 && closedEdgeY > 0){ + corner_radius = corner_radius_br; + }else if(closedEdgeX < 0 && closedEdgeY > 0){ + corner_radius = corner_radius_bl; + } + if(closedEdgeXabs > corner_radius && closedEdgeYabs > corner_radius){ + return pixel; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; } - + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeXabs > corner_radius){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeYabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + if(closedEdgeYabs > corner_radius){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (closedEdgeXabs / border_thickness)*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return pixel; + } + } + float d = distance(float2(closedEdgeXabs, closedEdgeYabs), float2(corner_radius,corner_radius)); + if(dAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Open-OBSSourceProjector { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenSourceProjector')] -[Alias('obs.powershell.websocket.OpenSourceProjector')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('projectorGeometry')] -[string] -$ProjectorGeometry, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57363,115 +53890,331 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Open-OBSVideoMixProjector { - +function Get-OBSRoundedRectPerSideShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OpenVideoMixProjector')] -[Alias('obs.powershell.websocket.OpenVideoMixProjector')] +[Alias('Set-OBSRoundedRectPerSideShader','Add-OBSRoundedRectPerSideShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('videoMixType')] -[string] -$VideoMixType, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorIndex')] -[double] -$MonitorIndex, - +# Set the corner_radius_bottom of OBSRoundedRectPerSideShader +[Alias('corner_radius_bottom')] +[ComponentModel.DefaultBindingProperty('corner_radius_bottom')] +[Int32] +$CornerRadiusBottom, +# Set the corner_radius_left of OBSRoundedRectPerSideShader +[Alias('corner_radius_left')] +[ComponentModel.DefaultBindingProperty('corner_radius_left')] +[Int32] +$CornerRadiusLeft, +# Set the corner_radius_top of OBSRoundedRectPerSideShader +[Alias('corner_radius_top')] +[ComponentModel.DefaultBindingProperty('corner_radius_top')] +[Int32] +$CornerRadiusTop, +# Set the corner_radius_right of OBSRoundedRectPerSideShader +[Alias('corner_radius_right')] +[ComponentModel.DefaultBindingProperty('corner_radius_right')] +[Int32] +$CornerRadiusRight, +# Set the border_thickness of OBSRoundedRectPerSideShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectPerSideShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the border_alpha_start of OBSRoundedRectPerSideShader +[Alias('border_alpha_start')] +[ComponentModel.DefaultBindingProperty('border_alpha_start')] +[Single] +$BorderAlphaStart, +# Set the border_alpha_end of OBSRoundedRectPerSideShader +[Alias('border_alpha_end')] +[ComponentModel.DefaultBindingProperty('border_alpha_end')] +[Single] +$BorderAlphaEnd, +# Set the alpha_cut_off of OBSRoundedRectPerSideShader +[Alias('alpha_cut_off')] +[ComponentModel.DefaultBindingProperty('alpha_cut_off')] +[Single] +$AlphaCutOff, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('projectorGeometry')] -[string] -$ProjectorGeometry, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect_per_side' +$ShaderNoun = 'OBSRoundedRectPerSideShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int corner_radius_bottom< + string label = "Corner radius bottom"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_left< + string label = "Corner radius left"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_top< + string label = "Corner radius top"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int corner_radius_right< + string label = "Corner radius right"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; +uniform float border_alpha_start< + string label = "border alpha start"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 1.0; +uniform float border_alpha_end< + string label = "border alpha end"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float alpha_cut_off< + string label = "alpha cut off"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float4 output_color = image.Sample(textureSampler, v_in.uv); + int closedEdgeX = 0; + int closedEdgeY = 0; + if(output_color.a < alpha_cut_off){ + return float4(1.0,0.0,0.0,0.0); + } + if(image.Sample(textureSampler, v_in.uv + float2(corner_radius_right*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = corner_radius_right; + }else if(image.Sample(textureSampler, v_in.uv + float2(-corner_radius_left*uv_pixel_interval.x,0)).a < alpha_cut_off){ + closedEdgeX = -corner_radius_left; + } + if(image.Sample(textureSampler, v_in.uv + float2(0,corner_radius_bottom*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = corner_radius_bottom; + }else if(image.Sample(textureSampler, v_in.uv + float2(0,-corner_radius_top*uv_pixel_interval.y)).a < alpha_cut_off){ + closedEdgeY = -corner_radius_top; + } + if(closedEdgeX == 0 && closedEdgeY == 0){ + return output_color; + } + if(closedEdgeX != 0){ + [loop] for(int x = 1;x corner_radius){ + closedEdgeXabs = 0; + } + if(closedEdgeYabs > corner_radius){ + closedEdgeYabs = 0; + } + if(closedEdgeXabs == 0 && closedEdgeYabs == 0){ + return output_color; + } + if(closedEdgeXabs == 0){ + if(closedEdgeYabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeYabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } + if(closedEdgeYabs == 0){ + if(closedEdgeXabs <= border_thickness){ + float4 fade_color = border_color; + fade_color.a = border_alpha_end + (float(closedEdgeXabs) / float(border_thickness))*(border_alpha_start-border_alpha_end); + return fade_color; + }else{ + return output_color; + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float closest = closedEdgeXabsAdd|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57480,110 +54223,183 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSInput { - +function Get-OBSRoundedRectShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveInput')] -[Alias('obs.powershell.websocket.RemoveInput')] +[Alias('Set-OBSRoundedRectShader','Add-OBSRoundedRectShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the corner_radius of OBSRoundedRectShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedRectShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the border_color of OBSRoundedRectShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_rect' +$ShaderNoun = 'OBSRoundedRectShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_color; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + float2 mirrored_tex_coord = float2(0.5, 0.5) - abs(v_in.uv - float2(0.5, 0.5)); + float4 output_color = image.Sample(textureSampler, v_in.uv); + + float2 pixel_position = float2(mirrored_tex_coord.x / uv_pixel_interval.x, mirrored_tex_coord.y / uv_pixel_interval.y); + float pixel_distance_from_center = distance(pixel_position, float2(corner_radius, corner_radius)); + + bool is_in_corner = pixel_position.x < corner_radius && pixel_position.y < corner_radius; + bool is_within_radius = pixel_distance_from_center <= corner_radius; + + bool is_within_edge_border = !is_in_corner && (pixel_position.x < 0 && pixel_position.x >= -border_thickness || pixel_position.y < 0 && pixel_position.y >= -border_thickness); + bool is_within_corner_border = is_in_corner && pixel_distance_from_center > corner_radius && pixel_distance_from_center <= (corner_radius + border_thickness); + + return ((!is_in_corner || is_within_radius)?output_color:float4(0,0,0,0)) + ((is_within_edge_border || is_within_corner_border)?border_color:float4(0,0,0,0)); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57592,335 +54408,365 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSProfile { - +function Get-OBSRoundedStrokeGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveProfile')] -[Alias('obs.powershell.websocket.RemoveProfile')] +[Alias('Set-OBSRoundedStrokeGradientShader','Add-OBSRoundedStrokeGradientShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the corner_radius of OBSRoundedStrokeGradientShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeGradientShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeGradientShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the rotation_speed of OBSRoundedStrokeGradientShader +[Alias('rotation_speed')] +[ComponentModel.DefaultBindingProperty('rotation_speed')] +[Int32] +$RotationSpeed, +# Set the border_colorL of OBSRoundedStrokeGradientShader +[Alias('border_colorL')] +[ComponentModel.DefaultBindingProperty('border_colorL')] +[String] +$BorderColorL, +# Set the border_colorR of OBSRoundedStrokeGradientShader +[Alias('border_colorR')] +[ComponentModel.DefaultBindingProperty('border_colorR')] +[String] +$BorderColorR, +# Set the center_width of OBSRoundedStrokeGradientShader +[Alias('center_width')] +[ComponentModel.DefaultBindingProperty('center_width')] +[Int32] +$CenterWidth, +# Set the center_height of OBSRoundedStrokeGradientShader +[Alias('center_height')] +[ComponentModel.DefaultBindingProperty('center_height')] +[Int32] +$CenterHeight, +# Set the notes of OBSRoundedStrokeGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_stroke_gradient' +$ShaderNoun = 'OBSRoundedStrokeGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "Border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int rotation_speed< + string label = "rotation speed"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform float4 border_colorL; +uniform float4 border_colorR; +//uniform float color_spread = 2.0; +uniform int center_width< + string label = "center width"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_height< + string label = "center height"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; +// float3 hsv2rgb(float3 c) +// { +// float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); +// float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); +// return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +// } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float mod(float x, float y) +{ + return x - y * floor(x/y); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float4 gradient(float c) { + c = mod(c , 2.0); + if(c < 0.0f){ + c = c * -1.0; + } + if(c > 1.0){ + c = 1.0 - c; + if(c < 0.0f){ + c = c + 1.0; } + } + return lerp(border_colorL, border_colorR, c); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 getBorderColor(float2 toCenter){ + float angle = atan2(toCenter.y ,toCenter.x ); + float angleMod = (elapsed_time * mod(float(rotation_speed) , 18.0)) / 9; + return gradient((angle / 3.14159265f) + angleMod); +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 st = v_in.uv * uv_scale; + float2 center_pixel_coordinates = float2((float(center_width) * 0.01), (float(center_height) * 0.01) ); + float2 toCenter = center_pixel_coordinates - st; + + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return output_color; + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; + } + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return getBorderColor(toCenter); + } + else + { + return output_color; } + } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return getBorderColor(toCenter); } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSScene { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveScene')] -[Alias('obs.powershell.websocket.RemoveScene')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + else + { + return output_color; } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + } + return float4(0.0, 0.0, 0.0, 0.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSceneItem { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSceneItem')] -[Alias('obs.powershell.websocket.RemoveSceneItem')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -57929,217 +54775,287 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Remove-OBSSourceFilter { - +function Get-OBSRoundedStrokeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'RemoveSourceFilter')] -[Alias('obs.powershell.websocket.RemoveSourceFilter')] +[Alias('Set-OBSRoundedStrokeShader','Add-OBSRoundedStrokeShader')] param( - +# Set the corner_radius of OBSRoundedStrokeShader +[Alias('corner_radius')] +[ComponentModel.DefaultBindingProperty('corner_radius')] +[Int32] +$CornerRadius, +# Set the border_thickness of OBSRoundedStrokeShader +[Alias('border_thickness')] +[ComponentModel.DefaultBindingProperty('border_thickness')] +[Int32] +$BorderThickness, +# Set the minimum_alpha_percent of OBSRoundedStrokeShader +[Alias('minimum_alpha_percent')] +[ComponentModel.DefaultBindingProperty('minimum_alpha_percent')] +[Int32] +$MinimumAlphaPercent, +# Set the border_color of OBSRoundedStrokeShader +[Alias('border_color')] +[ComponentModel.DefaultBindingProperty('border_color')] +[String] +$BorderColor, +# Set the notes of OBSRoundedStrokeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'rounded_stroke' +$ShaderNoun = 'OBSRoundedStrokeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//rounded rectange shader from https://raw.githubusercontent.com/exeldro/obs-lua/master/rounded_rect.shader +//modified slightly by Surn +//Converted to OpenGl by Q-mii & Exeldro February 21, 2022 +uniform int corner_radius< + string label = "Corner radius"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 200; + int step = 1; +> = 0; +uniform int border_thickness< + string label = "border thickness"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform int minimum_alpha_percent< + string label = "Minimum alpha percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float4 border_color; +uniform string notes< + string widget_type = "info"; +> = "Outlines the opaque areas with a rounded border. Default Minimum Alpha Percent is 50%, lowering will reveal more"; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float min_alpha = clamp(minimum_alpha_percent * .01, -1.0, 101.0); + float4 output_color = image.Sample(textureSampler, v_in.uv); + if (output_color.a < min_alpha) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + int closedEdgeX = 0; + if (image.Sample(textureSampler, v_in.uv + float2(corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(-corner_radius * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = corner_radius; + } + int closedEdgeY = 0; + if (image.Sample(textureSampler, v_in.uv + float2(0, corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + else if (image.Sample(textureSampler, v_in.uv + float2(0, -corner_radius * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = corner_radius; + } + if (closedEdgeX == 0 && closedEdgeY == 0) + { + return float4(output_color); + } + if (closedEdgeX != 0) + { + [loop] + for (int x = 1; x < corner_radius; x++) + { + if (image.Sample(textureSampler, v_in.uv + float2(x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(-x * uv_pixel_interval.x, 0)).a < min_alpha) + { + closedEdgeX = x; + break; } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + } + if (closedEdgeY != 0) + { + [loop] + for (int y = 1; y < corner_radius; y++) + { + if (image.Sample(textureSampler, v_in.uv + float2(0, y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; + } + if (image.Sample(textureSampler, v_in.uv + float2(0, -y * uv_pixel_interval.y)).a < min_alpha) + { + closedEdgeY = y; + break; } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + if (closedEdgeX == 0) + { + if (closedEdgeY < border_thickness) + { + return border_color; } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + else + { + return float4(output_color); + } + } + if (closedEdgeY == 0) + { + if (closedEdgeX < border_thickness) + { + return border_color; + } + else + { + return float4(output_color); } + } + float d = distance(float2(closedEdgeX, closedEdgeY), float2(corner_radius, corner_radius)); + if (d < corner_radius) + { + if (corner_radius - d < border_thickness) + { + return border_color; + } + else + { + return output_color; + } + } + return float4(0.0, 0.0, 0.0, 0.0); } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Resume-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ResumeRecord')] -[Alias('obs.powershell.websocket.ResumeRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58148,100 +55064,265 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Save-OBSReplayBuffer { - +function Get-OBSScanLineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveReplayBuffer')] -[Alias('obs.powershell.websocket.SaveReplayBuffer')] +[Alias('Set-OBSScanLineShader','Add-OBSScanLineShader')] param( -# If set, will return the information that would otherwise be sent to OBS. +# Set the lengthwise of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('lengthwise')] +[Management.Automation.SwitchParameter] +$Lengthwise, +# Set the animate of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the speed of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the angle of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the shift of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('shift')] +[Management.Automation.SwitchParameter] +$Shift, +# Set the boost of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('boost')] +[Management.Automation.SwitchParameter] +$Boost, +# Set the floor of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('floor')] +[Single] +$Floor, +# Set the period of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('period')] +[Single] +$Period, +# Set the notes of OBSScanLineShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'scan_line' +$ShaderNoun = 'OBSScanLineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Scan Line Effect for OBS Studio +// originally from Andersama (https://github.com/Andersama) +// Modified and improved my Charles Fettinger (https://github.com/Oncorporation) 1/2019 +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +//Count the number of scanlines we want via height or width, adjusts the sin wave period +uniform bool lengthwise; +//Do we want the scanlines to move? +uniform bool animate; +//How fast do we want those scanlines to move? +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10000.0; + float step = 1; +> = 1000; +//What angle should the scanlines come in at (based in degrees) +uniform float angle< + string label = "angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 45; +//Turns on adjustment of the results, sin returns -1 -> 1 these settings will change the results a bit +//By default values for color range from 0 to 1 +//Boost centers the result of the sin wave on 1*, to help maintain the brightness of the screen +uniform bool shift = true; +uniform bool boost = true; +//Increases the minimum value of the sin wave +uniform float floor< + string label = "Floor"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.001; +> = 0.0; +//final adjustment to the period of the sin wave, we can''t / 0, need to be careful w/ user input +uniform float period< + string label = "Period"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 1000.0; + float step = 1.0; +> = 10.0; +uniform string notes< + string widget_type = "info"; +> = "floor affects the minimum opacity of the scan line"; +float4 mainImage(VertData v_in) : TARGET +{ + //3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481 3.141592653589793238462643383279502884197169399375105820974944592307816406286 + // float pix2 = 6.2831853071795864769252;//86766559005768394338798750211641949 + float nfloor = clamp(floor, 0.0, 100.0) * 0.01; + float nperiod = max(period, 1.0); + float gap = 1 - nfloor; + float pi = 3.1415926535897932384626; + float2 direction = float2( cos(angle * pi / 180.0) , sin(angle * pi / 180.0) ); + float nspeed = 0.0; + if(animate){ + nspeed = speed * 0.0001; + } + + float4 color = image.Sample(textureSampler, v_in.uv); + + float t = elapsed_time * nspeed; + + if(!lengthwise){ + float base_height = 1.0 / uv_pixel_interval.y; + float h_interval = pi * base_height; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (h_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } + else{ + float base_width = 1.0 / uv_pixel_interval.x; + float w_interval = pi * base_width; + + float rh_sin = sin(((v_in.uv.y * direction.y + v_in.uv.x * direction.x) + t) * (w_interval / nperiod)); + if(shift){ + rh_sin = ((1.0 + rh_sin) * 0.5) * gap + nfloor; + if(boost){ + rh_sin += gap * 0.5; + } + } + float4 s_mult = float4(rh_sin,rh_sin,rh_sin,1); + return s_mult * color; + } +} - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] + } } - if ($PassThru) { - [PSCustomObject]$requestPayload + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} + } + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58250,146 +55331,450 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Save-OBSSourceScreenshot { - +function Get-OBSSeascapeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SaveSourceScreenshot')] -[Alias('obs.powershell.websocket.SaveSourceScreenshot')] +[Alias('Set-OBSSeascapeShader','Add-OBSSeascapeShader')] param( - +# Set the AA of OBSSeascapeShader +[ComponentModel.DefaultBindingProperty('AA')] +[Management.Automation.SwitchParameter] +$AA, +# Set the SEA_HEIGHT of OBSSeascapeShader +[Alias('SEA_HEIGHT')] +[ComponentModel.DefaultBindingProperty('SEA_HEIGHT')] +[Single] +$SEAHEIGHT, +# Set the SEA_CHOPPY of OBSSeascapeShader +[Alias('SEA_CHOPPY')] +[ComponentModel.DefaultBindingProperty('SEA_CHOPPY')] +[Single] +$SEACHOPPY, +# Set the SEA_SPEED of OBSSeascapeShader +[Alias('SEA_SPEED')] +[ComponentModel.DefaultBindingProperty('SEA_SPEED')] +[Single] +$SEASPEED, +# Set the SEA_FREQ of OBSSeascapeShader +[Alias('SEA_FREQ')] +[ComponentModel.DefaultBindingProperty('SEA_FREQ')] +[Single] +$SEAFREQ, +# Set the SEA_BASE of OBSSeascapeShader +[Alias('SEA_BASE')] +[ComponentModel.DefaultBindingProperty('SEA_BASE')] +[String] +$SEABASE, +# Set the SEA_WATER_COLOR of OBSSeascapeShader +[Alias('SEA_WATER_COLOR')] +[ComponentModel.DefaultBindingProperty('SEA_WATER_COLOR')] +[String] +$SEAWATERCOLOR, +# Set the CAMERA_SPEED of OBSSeascapeShader +[Alias('CAMERA_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_SPEED')] +[Single] +$CAMERASPEED, +# Set the CAMERA_TURN_SPEED of OBSSeascapeShader +[Alias('CAMERA_TURN_SPEED')] +[ComponentModel.DefaultBindingProperty('CAMERA_TURN_SPEED')] +[Single] +$CAMERATURNSPEED, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFormat')] -[string] -$ImageFormat, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageFilePath')] -[string] -$ImageFilePath, +process { +$shaderName = 'seascape' +$ShaderNoun = 'OBSSeascapeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +/* + * "Seascape" by Alexander Alekseev aka TDM - 2014 + * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + * Contact: tdmaav@gmail.com + * https://www.shadertoy.com/view/Ms2SD1 adapted by Exeldro + */ -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageWidth')] -[ValidateRange(8,4096)] -[double] -$ImageWidth, +#define NUM_STEPS 8 +#define PI 3.141592 +#define EPSILON 0.001 +uniform bool AA< + string label = "Smooth (more resources)"; +> = false; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageHeight')] -[ValidateRange(8,4096)] -[double] -$ImageHeight, +#ifndef OPENGL +#define mat2 float2x2 +#define mat3 float3x3 +#define fract frac +#define mix lerp +#endif -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('imageCompressionQuality')] -[ValidateRange(-1,100)] -[double] -$ImageCompressionQuality, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +// sea +#define ITER_GEOMETRY 3 +#define ITER_FRAGMENT 5 +uniform float SEA_HEIGHT< + string label = "Sea Height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.5; + float step = 0.001; +> = 0.6; +uniform float SEA_CHOPPY< + string label = "Sea Choppy"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 4.0; +uniform float SEA_SPEED< + string label = "Sea Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.8; +uniform float SEA_FREQ< + string label = "Sea Frequency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.16; +uniform float4 SEA_BASE< + string label = "Sea Base"; +> = {0.0,0.09,0.18,1.0}; +uniform float4 SEA_WATER_COLOR< + string label = "Sea Water"; +> = {0.48,0.54,0.36,1.0}; +uniform float CAMERA_SPEED< + string label = "Camera Speed"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; -process { +uniform float CAMERA_TURN_SPEED< + string label = "Camera Turn Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float SEA_TIME(){ + return 1.0 + elapsed_time * SEA_SPEED; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +// math +mat3 fromEuler(float3 ang) { + float2 a1 = float2(sin(ang.x),cos(ang.x)); + float2 a2 = float2(sin(ang.y),cos(ang.y)); + float2 a3 = float2(sin(ang.z),cos(ang.z)); + return mat3(float3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x), + float3(-a2.y*a1.x,a1.y*a2.y,a2.x), + float3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y)); +} + +float hash(float2 p) { + float h = dot(p,float2(127.1,311.7)); + return fract(sin(h)*43758.5453123); +} + +float noise(float2 p) { + float2 i = floor( p ); + float2 f = fract( p ); + float2 u = f*f*(3.0-2.0*f); + return -1.0+2.0*mix( mix( hash( i + float2(0.0,0.0) ), + hash( i + float2(1.0,0.0) ), u.x), + mix( hash( i + float2(0.0,1.0) ), + hash( i + float2(1.0,1.0) ), u.x), u.y); +} + +// lighting +float diffuse(float3 n,float3 l,float p) { + return pow(dot(n,l) * 0.4 + 0.6,p); +} +float specular(float3 n,float3 l,float3 e,float s) { + float nrm = (s + 8.0) / (PI * 8.0); + return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; +} + +// sky +float3 getSkyColor(float3 e) { + e.y = (max(e.y,0.0)*0.8+0.2)*0.8; + return float3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +} + +// sea +float sea_octave(float2 uv, float choppy) { + uv += noise(uv); + float2 wv = 1.0-abs(sin(uv)); + float2 swv = abs(cos(uv)); + wv = mix(wv,swv,wv); + return pow(1.0-pow(wv.x * wv.y,0.65),choppy); +} + +float map(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; + uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_GEOMETRY; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float map_detailed(float3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + float2 uv = p.xz; uv.x *= 0.75; + mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + float st = SEA_TIME(); + float d, h = 0.0; + for(int i = 0; i < ITER_FRAGMENT; i++) { + d = sea_octave((uv+float2(st,st))*freq,choppy); + d += sea_octave((uv-float2(st,st))*freq,choppy); + h += d * amp; + uv = mul(uv, octave_m); + freq *= 1.9; + amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float3 getSeaColor(float3 p, float3 n, float3 l, float3 eye, float3 dist) { + float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); + fresnel = min(pow(fresnel,3.0), 0.5); + + float3 reflected = getSkyColor(reflect(eye,n)); + float3 refracted = SEA_BASE.rgb + diffuse(n,l,80.0) * SEA_WATER_COLOR.rgb * 0.12; + + float3 color = mix(refracted,reflected,fresnel); + + float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); + color += SEA_WATER_COLOR.rgb * (p.y - SEA_HEIGHT) * 0.18 * atten; + + float s = specular(n,l,eye,60.0); + color += float3(s,s,s); + + return color; +} + +// tracing +float3 getNormal(float3 p, float eps) { + float3 n; + n.y = map_detailed(p); + n.x = map_detailed(float3(p.x+eps,p.y,p.z)) - n.y; + n.z = map_detailed(float3(p.x,p.y,p.z+eps)) - n.y; + n.y = eps; + return normalize(n); +} + +float heightMapTracing(float3 ori, float3 dir, out float3 p) { + float tm = 0.0; + float tx = 1000.0; + float hx = map(ori + dir * tx); + if(hx > 0.0) { + p = ori + dir * tx; + return tx; + } + float hm = map(ori + dir * tm); + float tmid = 0.0; + for(int i = 0; i < NUM_STEPS; i++) { + tmid = mix(tm,tx, hm/(hm-hx)); + p = ori + dir * tmid; + float hmid = map(p); + if(hmid < 0.0) { + tx = tmid; + hx = hmid; + } else { + tm = tmid; + hm = hmid; } + } + return tmid; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float3 getPixel(in float2 coord, float time) { + float2 uv = coord / uv_size.xy; + uv = uv * 2.0 - 1.0; + uv.x *= uv_size.x / uv_size.y; + + // ray + float3 ang = float3(sin(time*3.0*CAMERA_TURN_SPEED)*0.1,sin(time*CAMERA_TURN_SPEED)*0.2+0.3,time*CAMERA_TURN_SPEED); + float3 ori = float3(0.0,3.5,time*5.0*CAMERA_SPEED); + float3 dir = normalize(float3(uv.xy,-2.0)); + dir.z += length(uv) * 0.14; + dir = mul(normalize(dir), fromEuler(ang)); + + // tracing + float3 p; + heightMapTracing(ori,dir,p); + float3 dist = p - ori; + float3 n = getNormal(p, dot(dist,dist) * (0.1 / uv_size.x)); + float3 light = normalize(float3(0.0,1.0,0.8)); + + // color + return mix( + getSkyColor(dir), + getSeaColor(p,n,light,dir,dist), + pow(smoothstep(0.0,-0.02,dir.y),0.2)); +} + +// main +float4 mainImage(VertData v_in) : TARGET +{ + float time = elapsed_time * 0.3; + float2 fragCoord = float2(v_in.uv.x * uv_size.x, (1.0 - v_in.uv.y) * uv_size.y); + + float3 color = float3(0.0,0.0,0.0);; + if (AA){ + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + float2 uv = fragCoord+float2(i,j)/3.0; + color += getPixel(uv, time); } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + color /= 9.0; + }else{ + color = getPixel(fragCoord, time); + } + + // post + return float4(pow(color,float3(0.65,0.65,0.65)), 1.0); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat + } + } +} - Get-Item $paramCopy["imageFilePath"] | - Add-Member NoteProperty InputName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty SourceName $paramCopy["SourceName"] -Force -PassThru | - Add-Member NoteProperty ImageWidth $paramCopy["ImageWidth"] -Force -PassThru | - Add-Member NoteProperty ImageHeight $paramCopy["ImageHeight"] -Force -PassThru - } @@ -58397,116 +55782,194 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSCallVendorRequest { - +function Get-OBSSeasickShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'CallVendorRequest')] -[Alias('obs.powershell.websocket.CallVendorRequest')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] +[Alias('Set-OBSSeasickShader','Add-OBSSeasickShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('vendorName')] -[string] -$VendorName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestType')] -[string] -$RequestType, - +# Set the notes of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the amplitude of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the speed of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the frequency of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('frequency')] +[Single] +$Frequency, +# Set the opacity of OBSSeasickShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('requestData')] -[PSObject] -$RequestData, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'seasick' +$ShaderNoun = 'OBSSeasickShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Seasick - an effect for OBS Studio +// +uniform string notes< + string widget_type = "info"; +> = "Seasick - from the game Snavenger\n\n(available on Google Play/Amazon Fire)"; +uniform float amplitude< + string label = "amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.03; +uniform float speed< + string label = "speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 1.0; +uniform float frequency< + string label = "frequency"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 6.0; +uniform float opacity< + string label = "opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +float4 mainImage(VertData v_in) : TARGET +{ + float2 pulse = sin(elapsed_time*speed - frequency * v_in.uv); + float2 coord = v_in.uv + amplitude * float2(pulse.x, -pulse.y); + return image.Sample(textureSampler, coord) * opacity; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58515,105 +55978,305 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSCustomEvent { - +function Get-OBSSelectiveColorShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'BroadcastCustomEvent')] -[Alias('obs.powershell.websocket.BroadcastCustomEvent')] +[Alias('Set-OBSSelectiveColorShader','Add-OBSSelectiveColorShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('eventData')] -[PSObject] -$EventData, -# If set, will return the information that would otherwise be sent to OBS. +# Set the cutoff_Red of OBSSelectiveColorShader +[Alias('cutoff_Red')] +[ComponentModel.DefaultBindingProperty('cutoff_Red')] +[Single] +$CutoffRed, +# Set the cutoff_Green of OBSSelectiveColorShader +[Alias('cutoff_Green')] +[ComponentModel.DefaultBindingProperty('cutoff_Green')] +[Single] +$CutoffGreen, +# Set the cutoff_Blue of OBSSelectiveColorShader +[Alias('cutoff_Blue')] +[ComponentModel.DefaultBindingProperty('cutoff_Blue')] +[Single] +$CutoffBlue, +# Set the cutoff_Yellow of OBSSelectiveColorShader +[Alias('cutoff_Yellow')] +[ComponentModel.DefaultBindingProperty('cutoff_Yellow')] +[Single] +$CutoffYellow, +# Set the acceptance_Amplifier of OBSSelectiveColorShader +[Alias('acceptance_Amplifier')] +[ComponentModel.DefaultBindingProperty('acceptance_Amplifier')] +[Single] +$AcceptanceAmplifier, +# Set the show_Red of OBSSelectiveColorShader +[Alias('show_Red')] +[ComponentModel.DefaultBindingProperty('show_Red')] +[Management.Automation.SwitchParameter] +$ShowRed, +# Set the show_Green of OBSSelectiveColorShader +[Alias('show_Green')] +[ComponentModel.DefaultBindingProperty('show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the show_Blue of OBSSelectiveColorShader +[Alias('show_Blue')] +[ComponentModel.DefaultBindingProperty('show_Blue')] +[Management.Automation.SwitchParameter] +$ShowBlue, +# Set the show_Yellow of OBSSelectiveColorShader +[Alias('show_Yellow')] +[ComponentModel.DefaultBindingProperty('show_Yellow')] +[Management.Automation.SwitchParameter] +$ShowYellow, +# Set the notes of OBSSelectiveColorShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the background_type of OBSSelectiveColorShader +[Alias('background_type')] +[ComponentModel.DefaultBindingProperty('background_type')] +[Int32] +$BackgroundType, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'selective_color' +$ShaderNoun = 'OBSSelectiveColorShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Selective Color shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +//https://github.com/Oncorporation/obs-shaderfilter +//updated 4/13/2020: take into account the opacity/alpha of input image -thanks Skeletonbow for suggestion +//Converted to OpenGL by Q-mii February 25, 2020 +uniform float cutoff_Red< + string label = "cutoff Red"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.40; +uniform float cutoff_Green< + string label = "cutoff Green"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.025; +uniform float cutoff_Blue< + string label = "cutoff Blue"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.25; +uniform float cutoff_Yellow< + string label = "cutoff Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.25; +uniform float acceptance_Amplifier< + string label = "acceptance Amplifier"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 20.0; + float step = 0.001; +> = 5.0; + +uniform bool show_Red = true; +uniform bool show_Green = true; +uniform bool show_Blue = true; +uniform bool show_Yellow = true; +uniform string notes< + string widget_type = "info"; +> = "defaults: .4,.03,.25,.25, 5.0, true,true, true, true. cuttoff higher = less color, 0 = all 1 = none."; +uniform int background_type< + string label = "background type"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "Grey"; + int option_1_value = 1; + string option_1_label = "Luma"; + int option_2_value = 2; + string option_2_label = "White"; + int option_3_value = 3; + string option_3_label = "Black"; + int option_4_value = 4; + string option_4_label = "Transparent"; + int option_5_value = 5; + string option_5_label = "Background Color"; +> = 0; + +float4 mainImage(VertData v_in) : TARGET +{ + const float PI = 3.1415926535897932384626433832795;//acos(-1); + const float3 coefLuma = float3(0.2126, 0.7152, 0.0722); + float4 color = image.Sample(textureSampler, v_in.uv); + + float luminance = dot(coefLuma, color.rgb); + float4 gray = float4(luminance, luminance, luminance, 1.0); + + if (background_type == 0) + { + luminance = (color.r + color.g + color.b) * 0.3333; + gray = float4(luminance,luminance,luminance, 1.0); + } + //if (background_type == 1) + //{ + // gray = float4(luminance,luminance,luminance, 1.0); + //} + if (background_type == 2) + gray = float4(1.0,1.0,1.0,1.0); + if (background_type == 3) + gray = float4(0.0,0.0,0.0,1.0); + if (background_type == 4) + gray.a = 0.01; + if (background_type == 5) + gray = color; + + float redness = max ( min ( color.r - color.g , color.r - color.b ) / color.r , 0); + float greenness = max ( min ( color.g - color.r , color.g - color.b ) / color.g , 0); + float blueness = max ( min ( color.b - color.r , color.b - color.g ) / color.b , 0); + + float rgLuminance = (color.r*1.4 + color.g*0.6)/2; + float rgDiff = abs(color.r-color.g)*1.4; + + float yellowness = 0.1 + rgLuminance * 1.2 - color.b - rgDiff; + + float4 accept; + accept.r = float(show_Red) * (redness - cutoff_Red); + accept.g = float(show_Green) * (greenness - cutoff_Green); + accept.b = float(show_Blue) * (blueness - cutoff_Blue); + accept[3] = float(show_Yellow) * (yellowness - cutoff_Yellow); + + float acceptance = max (accept.r, max(accept.g, max(accept.b, max(accept[3],0)))); + float modAcceptance = min (acceptance * acceptance_Amplifier, 1); + + float4 result = color; + if (result.a > 0) { + result = modAcceptance * color + (1.0 - modAcceptance) * gray; + //result.a *= gray.a; + } + return result; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58622,217 +56285,346 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSOffsetMediaInputCursor { - +function Get-OBSShakeShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'OffsetMediaInputCursor')] -[Alias('obs.powershell.websocket.OffsetMediaInputCursor')] +[Alias('Set-OBSShakeShader','Add-OBSShakeShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the ViewProj of OBSShakeShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSShakeShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSShakeShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSShakeShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSShakeShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSShakeShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSShakeShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSShakeShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the local_time of OBSShakeShader +[Alias('local_time')] +[ComponentModel.DefaultBindingProperty('local_time')] +[Single] +$LocalTime, +# Set the random_scale of OBSShakeShader +[Alias('random_scale')] +[ComponentModel.DefaultBindingProperty('random_scale')] +[Single] +$RandomScale, +# Set the worble of OBSShakeShader +[ComponentModel.DefaultBindingProperty('worble')] +[Management.Automation.SwitchParameter] +$Worble, +# Set the speed of OBSShakeShader +[ComponentModel.DefaultBindingProperty('speed')] +[Single] +$Speed, +# Set the min_growth_pixels of OBSShakeShader +[Alias('min_growth_pixels')] +[ComponentModel.DefaultBindingProperty('min_growth_pixels')] +[Single] +$MinGrowthPixels, +# Set the max_growth_pixels of OBSShakeShader +[Alias('max_growth_pixels')] +[ComponentModel.DefaultBindingProperty('max_growth_pixels')] +[Single] +$MaxGrowthPixels, +# Set the randomize_movement of OBSShakeShader +[Alias('randomize_movement')] +[ComponentModel.DefaultBindingProperty('randomize_movement')] +[Management.Automation.SwitchParameter] +$RandomizeMovement, +# Set the notes of OBSShakeShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursorOffset')] -[double] -$MediaCursorOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'shake' +$ShaderNoun = 'OBSShakeShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Shake Effect By Charles Fettinger (https://github.com/Oncorporation) 2/2019 +// Added some randomization based upon random_scale input +// updated for latest version of obs-shaderfilter +uniform float4x4 ViewProj; +uniform texture2d image; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; +uniform float local_time; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float random_scale< + string label = "random scale"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 0.25; +uniform bool worble = false; +uniform float speed< + string label = "Speed"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.001; +> = 1.0; +uniform float min_growth_pixels< + string label = "min growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = -2.0; +uniform float max_growth_pixels< + string label = "max growth pixels"; + string widget_type = "slider"; + float minimum = -10.0; + float maximum = 10.0; + float step = 0.001; +> = 2.0; +uniform bool randomize_movement = false; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform string notes< + string widget_type = "info"; +> =''keep the random_scale low for small (0.2-1) for small jerky movements and larger for less often big jumps''; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +//noise values in range if 0.0 to 1.0 + +float noise3D(float x, float y, float z) { + float ptr = 0.0f; + return frac(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f); } +VertData mainTransform(VertData v_in) +{ + VertData vert_out; -} + float3 pos = v_in.pos.xyz; + float t; + float s; + float noise; + if (randomize_movement) + { + t = (rand_f * 2) - 1.0f; + s = (1 - rand_f * 2) - 1.0f;; + noise = clamp( rand_f * random_scale,-0.99, 0.99); + } + else + { + t = (1 + sin(elapsed_time * speed)) / 2; + s = (1 + cos(elapsed_time * speed)) / 2; + noise = clamp(noise3D(t,s,100) * random_scale,-0.99, 0.99); + } - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSPauseRecord { + float3 direction_from_center = float3((v_in.uv.x - 0.5 + noise) * uv_pixel_interval.y / uv_pixel_interval.x, v_in.uv.y - 0.5 + noise, 1); + float3 min_pos; + float3 max_pos; + if (worble) + { + min_pos = pos + direction_from_center * min_growth_pixels * 0.5; + max_pos = pos + direction_from_center * max_growth_pixels * 0.5; + } + else + { + min_pos = pos + direction_from_center * 0.5; + max_pos = min_pos; + } + float3 current_pos = min_pos * (1 - t) + max_pos * t; + //current_pos.x = v_in.pos.x + (t * min_pos.x); + current_pos.y = (min_pos.y * (1 - s) + max_pos.y * s); + //current_pos.y = v_in.pos.y + (s * min_pos.y); + //current_pos.z = min_pos.z * (1 - s) + max_pos.z * s; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PauseRecord')] -[Alias('obs.powershell.websocket.PauseRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float2 offset = uv_offset; + offset.x = uv_offset.x * (1 - t + noise); + offset.y = uv_offset.y * (1 - s + noise); + vert_out.pos = mul(float4(current_pos, 1), ViewProj); + + //float2 scale = uv_scale; + //scale += dot(pos - current_pos, 1); -process { + vert_out.uv = v_in.uv * uv_scale + offset; + return vert_out; +} +float4 mainImage(VertData v_in) : TARGET +{ + return image.Sample(textureSampler, v_in.uv); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -58841,229 +56633,371 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSPressInputPropertiesButton { - +function Get-OBSShineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'PressInputPropertiesButton')] -[Alias('obs.powershell.websocket.PressInputPropertiesButton')] +[Alias('Set-OBSShineShader','Add-OBSShineShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the l_tex of OBSShineShader +[Alias('l_tex')] +[ComponentModel.DefaultBindingProperty('l_tex')] +[String] +$LTex, +# Set the shine_color of OBSShineShader +[Alias('shine_color')] +[ComponentModel.DefaultBindingProperty('shine_color')] +[String] +$ShineColor, +# Set the speed_percent of OBSShineShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the gradient_percent of OBSShineShader +[Alias('gradient_percent')] +[ComponentModel.DefaultBindingProperty('gradient_percent')] +[Int32] +$GradientPercent, +# Set the delay_percent of OBSShineShader +[Alias('delay_percent')] +[ComponentModel.DefaultBindingProperty('delay_percent')] +[Int32] +$DelayPercent, +# Set the Apply_To_Alpha_Layer of OBSShineShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the ease of OBSShineShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the hide of OBSShineShader +[ComponentModel.DefaultBindingProperty('hide')] +[Management.Automation.SwitchParameter] +$Hide, +# Set the reverse of OBSShineShader +[ComponentModel.DefaultBindingProperty('reverse')] +[Management.Automation.SwitchParameter] +$Reverse, +# Set the One_Direction of OBSShineShader +[Alias('One_Direction')] +[ComponentModel.DefaultBindingProperty('One_Direction')] +[Management.Automation.SwitchParameter] +$OneDirection, +# Set the glitch of OBSShineShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSShineShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# Set the start_adjust of OBSShineShader +[Alias('start_adjust')] +[ComponentModel.DefaultBindingProperty('start_adjust')] +[Single] +$StartAdjust, +# Set the stop_adjust of OBSShineShader +[Alias('stop_adjust')] +[ComponentModel.DefaultBindingProperty('stop_adjust')] +[Single] +$StopAdjust, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('propertyName')] -[string] -$PropertyName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'shine' +$ShaderNoun = 'OBSShineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Shine Shader By Charles Fettinger (https://github.com/Oncorporation) 3/2019 +// use color to control shine amount, use transition wipes or make your own alpha texture +// slerp not currently used, for circular effects +//Converted to OpenGL by Exeldro February 14, 2022 +uniform texture2d l_tex; +uniform float4 shine_color ; +uniform int speed_percent< + string label = "speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 25; +uniform int gradient_percent< + string label = "gradient percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 20; +uniform int delay_percent< + string label = "delay percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool ease = false; +uniform bool hide = false; +uniform bool reverse = false; +uniform bool One_Direction = true; +uniform bool glitch = false; +uniform string notes< + string widget_type = "info"; +> = "Use Luma Wipes ( data/obs-plugins/obs-transitions/luma_wipes ) ''ease'' makes the animation pause at the begin and end for a moment, ''hide'' will make the image disappear, ''glitch'' is random and amazing, ''reverse'' quickly allows you to test settings, ''One Direction'' only shows the shine as it travels in one direction, ''delay percentage'' adds a delay between shines (requires adjustment to speed: https://www.desmos.com/calculator/wkgbndweyt )"; +uniform float start_adjust; +uniform float stop_adjust; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2.0; + if (t < 1.0) return -c/2.0 * (sqrt(1.0 - t*t) - 1.0) + b; + t -= 2.0; + return c/2.0 * (sqrt(1.0 - t*t) + 1.0) + b; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0.0,c,d); + return t; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 convert_pmalpha(float4 c) +{ + float4 ret = c; + if (c.a >= 0.001) + ret.xyz /= c.a; + else + ret = float4(0.0, 0.0, 0.0, 0.0); + return ret; +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float4 slerp(float4 start, float4 end, float percent) +{ + // Dot product - the cosine of the angle between 2 vectors. + float dotf = start.r*end.r+start.g*end.g+start.b*end.b+start.a*end.a; + // Clamp it to be in the range of Acos() + // This may be unnecessary, but floating point + // precision can be a fickle mistress. + dotf = clamp(dotf, -1.0f, 1.0f); + // Acos(dot) returns the angle between start and end, + // And multiplying that by percent returns the angle between + // start and the final result. + float theta = acos(dotf)*percent; + float4 RelativeVec = normalize(end - start * dotf); + + // Orthonormal basis + // The final result. + return ((start*cos(theta)) + (RelativeVec*sin(theta))); +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float4 mainImage(VertData v_in) : TARGET +{ + // convert input for vector math + float4 rgba = convert_pmalpha(image.Sample(textureSampler, v_in.uv)); + float speed = speed_percent * 0.01; + float softness = max(abs(gradient_percent * 0.01), 0.01) * sign(gradient_percent); + float delay = clamp(delay_percent * 0.01, 0.0, 1.0); + -} + // circular easing variable + float direction = abs(sin((elapsed_time - 0.001) * speed)); + float t = abs(sin(elapsed_time * speed)); + // if time is greater than direction, we are going up! + direction = t - direction; -} + // split into segments with frac or mod. + // delay is the gap between starting and ending of the sine wave, use speed to compensate + t = (frac(t) - delay) * (1 / (1 - delay)); + t = 1 + max(t,0.0); - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSSleep { + float s = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'Sleep')] -[Alias('obs.powershell.websocket.Sleep')] -param( + //if Unidirectional disable on return + if (One_Direction && (direction < 0.0)) + { + s = 0; + } + else + { + s = Styler(t, 0, c, d, ease); + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepMillis')] -[ValidateRange(0,50000)] -[double] -$SleepMillis, + // combine luma texture and user defined shine color + float luma = l_tex.Sample(textureSampler, v_in.uv).x; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sleepFrames')] -[ValidateRange(0,10000)] -[double] -$SleepFrames, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + // - adjust for min and max + if ((luma >= (start_adjust)) && (luma <= (1 - stop_adjust))) + { + if (reverse) + { + luma = 1.0 - luma; + } + + // user color with luma + float4 output_color = float4(shine_color.rgb, luma); -process { + float time = lerp(0.0f, 1.0f + abs(2*softness), s - 1.0); + // use luma texture to add alpha and shine - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + // if behind glow, consider trailing gradient shine then show underlying image + if (luma <= time - softness) + { + float alpha_behind = clamp(1.0 - (time - softness - luma ) / softness, 0.00, 1.0); + if (Apply_To_Alpha_Layer) + alpha_behind *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_behind); + } + + // if in front of glow, consider if the underlying image is hidden + if (luma >= time) + { + // if hide, make the transition better + if (hide) + { + return float4(rgba.rgb, lerp(0.0, rgba.a, (time + softness) / (1 + abs(2*softness)))); + } + else + { + return rgba; + } + } + + // else show the glow area, with luminance + float alpha_ = (time - luma) / softness; + if (Apply_To_Alpha_Layer) + alpha_ *= rgba.a; + return lerp(rgba, rgba + output_color, alpha_); + } + else + { + return rgba; + } +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -59072,237 +57006,288 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSStreamCaption { - +function Get-OBSSimpleGradientShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SendStreamCaption')] -[Alias('obs.powershell.websocket.SendStreamCaption')] +[Alias('Set-OBSSimpleGradientShader','Add-OBSSimpleGradientShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('captionText')] -[string] -$CaptionText, -# If set, will return the information that would otherwise be sent to OBS. +# Set the speed_percentage of OBSSimpleGradientShader +[Alias('speed_percentage')] +[ComponentModel.DefaultBindingProperty('speed_percentage')] +[Int32] +$SpeedPercentage, +# Set the alpha_percentage of OBSSimpleGradientShader +[Alias('alpha_percentage')] +[ComponentModel.DefaultBindingProperty('alpha_percentage')] +[Int32] +$AlphaPercentage, +# Set the Lens_Flair of OBSSimpleGradientShader +[Alias('Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Lens_Flair')] +[Management.Automation.SwitchParameter] +$LensFlair, +# Set the Animate_Lens_Flair of OBSSimpleGradientShader +[Alias('Animate_Lens_Flair')] +[ComponentModel.DefaultBindingProperty('Animate_Lens_Flair')] +[Management.Automation.SwitchParameter] +$AnimateLensFlair, +# Set the Apply_To_Alpha_Layer of OBSSimpleGradientShader +[Alias('Apply_To_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Apply_To_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$ApplyToAlphaLayer, +# Set the Apply_To_Specific_Color of OBSSimpleGradientShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# Set the Color_To_Replace of OBSSimpleGradientShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the notes of OBSSimpleGradientShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'simple_gradient' +$ShaderNoun = 'OBSSimpleGradientShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simple Gradient shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +//lots of room to play here +//Converted to OpenGL by Q-mii & Exeldro February 25, 2022 +uniform int speed_percentage< + string label = "speed percentage"; + string widget_type = "slider"; + int minimum = -500; + int maximum = 500; + int step = 1; +> = 240; // +uniform int alpha_percentage< + string label = "aplha percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 90; +uniform bool Lens_Flair = false; +uniform bool Animate_Lens_Flair = false; +uniform bool Apply_To_Alpha_Layer = false; +uniform bool Apply_To_Specific_Color; +uniform float4 Color_To_Replace; +uniform string notes< + string widget_type = "info"; +> = "This gradient is very basic from the top left corner. Red on horizontal, Green vertical, Blue Diagonal. Apply To Alpha Layer will add the gradient colors to the background. Lens Flair will brighten the scene from the bottom right. There is also a lot of unused code to play with in the shader file, delimted by /* ... */"; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByKeySequence { +float4 mainImage(VertData v_in) : TARGET +{ + float4 background_color = image.Sample(textureSampler, v_in.uv); + int no_colors = 4; + float3 colors[4]; + colors[0] = float3(1.0,0.0,0.0); + colors[1] = float3(0.0,1.0,0.0); + colors[2] = float3(0.0,0.0,1.0); + colors[3] = float3(1.0,1.0,1.0); + float alpha = float(alpha_percentage) * 0.01; + float speed = float(speed_percentage) * 0.01; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByKeySequence')] -[Alias('obs.powershell.websocket.TriggerHotkeyByKeySequence')] -param( + float mx = max(uv_size.x , uv_size.y); + //float2 uv = v_in.uv / mx; + float3 rgb = background_color.rgb; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyId')] -[string] -$KeyId, + // skip if (alpha is zero and only apply to alpha layer is true) + if (!(background_color.a <= 0.0 && Apply_To_Alpha_Layer == true)) + { + rgb = float3(v_in.uv.x, v_in.uv.y, 0.10 + 0.85 * sin(elapsed_time * speed)); + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers')] -[PSObject] -$KeyModifiers, + //create lens flare like effect + if (Lens_Flair) + { + float2 lens_flair_coordinates = float2(0.95 ,0.95); + if (Animate_Lens_Flair) + lens_flair_coordinates *= float2(sin(elapsed_time * speed) ,cos(elapsed_time * speed)); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.shift')] -[switch] -$KeyModifiersshift, + float dist = distance(v_in.uv, ( lens_flair_coordinates * uv_scale + uv_offset)); + for (int i = 0; i < no_colors; ++i) { + rgb += lerp(rgb, colors[i], dist * 1.5) * 0.25; + } + } -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.control')] -[switch] -$KeyModifierscontrol, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.alt')] -[switch] -$KeyModifiersalt, + //float3 col = colors[0]; +/* for (int i = 1; i < no_colors; ++i) { + float3 hole = float3( + sin(1.5 - distance(v_in.uv.x / mx, colors[i].x / mx) * 2.5 * speed), + sin(1.5 - distance(v_in.uv.y / mx, colors[i].y / mx) * 2.5 * speed), + colors[i].z); + rgb = lerp(rgb, hole, 0.1); +*/ +/* float3 hole = lerp(colors[i-1], colors[i], sin(elapsed_time * speed)); + col = lerp(col, hole, v_in.uv.x); +*/ + //} +// rgb = fflerp(rgb, col, 0.5); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('keyModifiers.command')] -[switch] -$KeyModifierscommand, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) -process { + //try prepositioned colors with colors[] array timing + //creates an animated color spotlight +/* int color_index = int(sin(elapsed_time * speed) * no_colors); + float3 start_color = colors[color_index]; + float3 end_color; + if (color_index >= 0) + { + end_color = colors[color_index - 1]; + } + else + { + end_color = colors[no_colors - 1]; + } + rgb = smoothstep(start_color, end_color, distance(v_in.uv , sin(elapsed_time * speed * no_colors) * (float2(1.0,1.0) * uv_scale + uv_offset))); +*/ + float4 rgba; + if (Apply_To_Alpha_Layer == false) + { + rgba = lerp(background_color,float4(rgb, 1.0),alpha); + } + else + { + rgba = lerp(background_color,background_color * float4(rgb,1.0), alpha); + } + if (Apply_To_Specific_Color) + { + float4 original_color = background_color; + background_color = (distance(background_color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : background_color; + rgba = lerp(original_color, background_color, clamp(alpha, 0, 1.0)); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -59311,329 +57296,370 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerHotkeyByName { - +function Get-OBSSimplexNoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerHotkeyByName')] -[Alias('obs.powershell.websocket.TriggerHotkeyByName')] +[Alias('Set-OBSSimplexNoiseShader','Add-OBSSimplexNoiseShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('hotkeyName')] -[string] -$HotkeyName, - +# Set the Snap_Percent of OBSSimplexNoiseShader +[Alias('Snap_Percent')] +[ComponentModel.DefaultBindingProperty('Snap_Percent')] +[Single] +$SnapPercent, +# Set the Speed_Percent of OBSSimplexNoiseShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Single] +$SpeedPercent, +# Set the Resolution of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Resolution')] +[Single] +$Resolution, +# Set the Fractal of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Fractal')] +[Management.Automation.SwitchParameter] +$Fractal, +# Set the Use_Alpha_Layer of OBSSimplexNoiseShader +[Alias('Use_Alpha_Layer')] +[ComponentModel.DefaultBindingProperty('Use_Alpha_Layer')] +[Management.Automation.SwitchParameter] +$UseAlphaLayer, +# Set the Fore_Color of OBSSimplexNoiseShader +[Alias('Fore_Color')] +[ComponentModel.DefaultBindingProperty('Fore_Color')] +[String] +$ForeColor, +# Set the Back_Color of OBSSimplexNoiseShader +[Alias('Back_Color')] +[ComponentModel.DefaultBindingProperty('Back_Color')] +[String] +$BackColor, +# Set the Alpha_Percent of OBSSimplexNoiseShader +[Alias('Alpha_Percent')] +[ComponentModel.DefaultBindingProperty('Alpha_Percent')] +[Single] +$AlphaPercent, +# Set the Notes of OBSSimplexNoiseShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('contextName')] -[string] -$ContextName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'simplex_noise' +$ShaderNoun = 'OBSSimplexNoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Simplex Noise shader by Charles Fettinger (https://github.com/Oncorporation) 5/2019 +// for use with obs-shaderfilter 1.0 +//based upon: https://www.shadertoy.com/view/XsX3zB +#ifndef OPENGL +#define fract frac +#endif - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float Snap_Percent< + string label = "Snap Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 7.5; +uniform float Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 2.5; +uniform float Resolution< + string label = "Resolution"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 16.0; +uniform bool Fractal = false; +uniform bool Use_Alpha_Layer = false; +uniform float4 Fore_Color = {0.95,0.95,0.95, 1.0}; +uniform float4 Back_Color = {0.75, 0.75, 0.75, 1.0}; +uniform float Alpha_Percent< + string label = "Alpha Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform string Notes< + string widget_type = "info"; +> = "Alpha Percentage applies to the shader, Use_Alpha_Layer applies effect with the image alpha layer, Resolution is the amount of detail of noise created.Fractal is a different algorithm. Snap Percent affects how many updates per second. Default values: 7.5%, 2.5%, 16.0, 100%"; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float dot(float3 a, float3 b){ + return a.r*b.r+a.g*b.g+a.b*b.b; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float dot4(float4 a, float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} +float snap(float x, float snap) +{ + return snap * round(x / max(0.01,snap)); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +float3 random3(float3 co) +{ + float j = 4096.0 * sin(dot(co, float3(17.0, 59.4, 15.0))); + float3 result; + result.z = fract(512.0 * j); + j *= .125; + result.x = fract(512.0 * j); + j *= .125; + result.y = fract(512.0 * j); + return result - 0.5; +} - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +/* 3d simplex noise */ +float simplex3d(float3 p) { + /* 1. find current tetrahedron T and it''s four vertices */ + /* s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices */ + /* x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertices*/ + + /* skew constants for 3d simplex functions */ + float F3 = 0.3333333; + float G3 = 0.1666667; -} + /* calculate s and x */ + float3 s = floor(p + dot(p, float3(F3,F3,F3))); + float3 x = p - s + dot(s, float3(G3,G3,G3)); + /* calculate i1 and i2 */ + float3 e = step(float3(0.0,0.0,0.0), x - x.yzx); + float3 i1 = e * (1.0 - e.zxy); + float3 i2 = 1.0 - e.zxy * (1.0 - e); -} + /* x1, x2, x3 */ + float3 x1 = x - i1 + G3; + float3 x2 = x - i2 + 2.0 * G3; + float3 x3 = x - 1.0 + 3.0 * G3; - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerMediaInputAction { + /* 2. find four surflets and store them in d */ + float4 w, d; + /* calculate surflet weights */ + w.x = dot(x, x); + w.y = dot(x1, x1); + w.z = dot(x2, x2); + w.w = dot(x3, x3); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerMediaInputAction')] -[Alias('obs.powershell.websocket.TriggerMediaInputAction')] -param( + /* w fades from 0.6 at the center of the surflet to 0.0 at the margin */ + w = max(0.61 - w, 0.0); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + /* calculate surflet components */ + d.x = dot(random3(s), x); + d.y = dot(random3(s + i1), x1); + d.z = dot(random3(s + i2), x2); + d.w = dot(random3(s + 1.0), x3); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, + /* multiply d by w^4 */ + w *= w; + w *= w; + d *= w; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaAction')] -[string] -$MediaAction, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + /* 3. return the sum of the four surflets */ + return dot4(d, float4(52.0, 52.0, 52.0, 52.0)); +} -process { +/* directional artifacts can be reduced by rotating each octave */ +float simplex3d_fractal(float3 m3) { + /* const matrices for 3d rotation */ +#ifdef OPENGL + float3x3 rot1 = float3x3( + float3(-0.37, 0.36, 0.85), + float3(-0.14, -0.93, 0.34), + float3(0.92, 0.01, 0.4 )); + float3x3 rot2 = float3x3( + float3(-0.55, -0.39, 0.74), + float3(0.33, -0.91, -0.24), + float3(0.77, 0.12, 0.63 )); + float3x3 rot3 = float3x3( + float3(-0.71, 0.52, -0.47), + float3(-0.08, -0.72, -0.68), + float3(-0.7, -0.45, 0.56 )); + float3 m = float3(m3.x, m3.y, m3.z); +#else + float3x3 rot1 = { + -0.37, 0.36, 0.85, + -0.14, -0.93, 0.34, + 0.92, 0.01, 0.4 }; + float3x3 rot2 = { + -0.55, -0.39, 0.74, + 0.33, -0.91, -0.24, + 0.77, 0.12, 0.63 }; + float3x3 rot3 = { + -0.71, 0.52, -0.47, + -0.08, -0.72, -0.68, + -0.7, -0.45, 0.56 }; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float3 m = {m3.x, m3.y, m3.z}; +#endif - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + return 0.5333333* simplex3d(mul(m, rot1)) + + 0.2666667 * simplex3d(2.0 * mul(m, rot2)) + + 0.1333333 * simplex3d(4.0 * mul(m, rot3)) + + 0.0666667 * simplex3d(8.0 * m); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + float time = snap(elapsed_time, Snap_Percent * .01); + float4 rgba = image.Sample(textureSampler, v_in.uv); + float2 p = v_in.uv.xy + float2( 0, -0.5); + float3 p3 = float3(p, time * (Speed_Percent * 0.01)); + + float pixel_alpha = 1.0; + // apply to mainImage rgba + if (Use_Alpha_Layer) { + p3 *= rgba.rgb; + pixel_alpha = rgba.a; + } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + float value; + + if (Fractal) { + value = simplex3d_fractal(p3 * (Resolution * 0.5) + (Resolution * 0.5)); + } + else { + value = simplex3d(p3 * Resolution); + } + + //soften color + value = 0.5 + (0.5 * value); + float intensity = dot(float3(value, value, value), float3(0.299, 0.587, 0.114)); + + //use intensity to apply foreground and background colors + float4 r = lerp(float4(float3(value, value, value), pixel_alpha), Fore_Color, saturate(intensity)); + r = lerp(Back_Color, r, saturate(intensity)); + r.a = pixel_alpha; + + return lerp(rgba, r, Alpha_Percent * 0.01); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Send-OBSTriggerStudioModeTransition { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'TriggerStudioModeTransition')] -[Alias('obs.powershell.websocket.TriggerStudioModeTransition')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -59642,110 +57668,222 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentPreviewScene { - +function Get-OBSSmartDenoiseShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentPreviewScene')] -[Alias('obs.powershell.websocket.SetCurrentPreviewScene')] +[Alias('Set-OBSSmartDenoiseShader','Add-OBSSmartDenoiseShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the uSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uSigma')] +[Single] +$USigma, +# Set the uKSigma of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uKSigma')] +[Single] +$UKSigma, +# Set the uThreshold of OBSSmartDenoiseShader +[ComponentModel.DefaultBindingProperty('uThreshold')] +[Single] +$UThreshold, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'smart_denoise' +$ShaderNoun = 'OBSSmartDenoiseShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Smart DeNoise By Michele Morrone (https://github.com/BrutPitt/glslSmartDeNoise) +// Converted to OBS version of HLSL by Euiko on February 10, 2025 + +#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI +#define INV_PI 0.31830988618379067153776752674503 +uniform float uSigma< + string label = "Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 3; // max based on the webgl sample, which is 3 + float step = 0.01; +> = 5.0; // default value based on shadertoy +uniform float uKSigma< + string label = "K-Sigma"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 24; // max based on the webgl sample, which is 24 + float step = 0.01; +> = 7.0; // the default value is based on the webgl sample +uniform float uThreshold< + string label = "Edge Threshold"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 2; // max based on the webgl sample, which is 2 + float step = 0.01; +> = 0.190; // the default value is based on the webgl sample - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// smartDeNoise - parameters +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// float2 uv - actual fragment coord +// float2 size - window size +// float sigma > 0 - sigma Standard Deviation +// float kSigma >= 0 - sigma coefficient +// kSigma * sigma --> radius of the circular kernel +// float threshold - edge sharpening threshold +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// NOTE: image''s texture2d data will be supplied by the OBS shaderfilter by default +float4 smartDeNoise(float2 uv, float2 size, float sigma, float kSigma, float threshold) +{ + float radius = round(kSigma * sigma); + float radQ = radius * radius; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + float invSigmaQx2 = 0.5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) + float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2) + + float invThresholdSqx2 = 0.5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) + float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2) + + float4 centrPx = image.Sample(textureSampler, uv); + + float zBuff = 0.0; + float4 aBuff = float4(0.0, 0.0, 0.0, 0.0); + + float2 d; + for (d.x = -radius; d.x <= radius; d.x += 1.0) + { + float pt = sqrt(radQ - (d.x * d.x)); // pt = yRadius: have circular trend + d.y = -pt; + for (; d.y <= pt; d.y += 1.0) + { + float blurFactor = exp((-dot(d, d)) * invSigmaQx2) * invSigmaQx2PI; + float4 walkPx = image.Sample(textureSampler, uv + (d / size)); + float4 dC = walkPx - centrPx; + float deltaFactor = (exp((-dot(dC.xyz, dC.xyz)) * invThresholdSqx2) * invThresholdSqrt2PI) * blurFactor; + zBuff += deltaFactor; + aBuff += (walkPx * deltaFactor); } + } + return aBuff / float4(zBuff, zBuff, zBuff, zBuff); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + return smartDeNoise(v_in.uv, uv_size, uSigma, uKSigma, uThreshold); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -59754,217 +57892,270 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProfile { - +function Get-OBSSpecularShineShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProfile')] -[Alias('obs.powershell.websocket.SetCurrentProfile')] +[Alias('Set-OBSSpecularShineShader','Add-OBSSpecularShineShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('profileName')] -[string] -$ProfileName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Hint of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('Hint')] +[String] +$Hint, +# Set the roughness of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('roughness')] +[Single] +$Roughness, +# Set the lightStrength of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('lightStrength')] +[Single] +$LightStrength, +# Set the LightPositionX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionX')] +[Single] +$LightPositionX, +# Set the LightPositionY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('LightPositionY')] +[Single] +$LightPositionY, +# Set the flattenNormal of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('flattenNormal')] +[Single] +$FlattenNormal, +# Set the stretchNormalX of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalX')] +[Single] +$StretchNormalX, +# Set the stretchNormalY of OBSSpecularShineShader +[ComponentModel.DefaultBindingProperty('stretchNormalY')] +[Single] +$StretchNormalY, +# Set the Light_Color of OBSSpecularShineShader +[Alias('Light_Color')] +[ComponentModel.DefaultBindingProperty('Light_Color')] +[Single[]] +$LightColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'specular-shine' +$ShaderNoun = 'OBSSpecularShineShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Specular Shine shader by Andicraft / Andrea Jörgensen - https://github.com/Andicraft + +uniform string Hint< + string widget_type = "info"; +> = "Try using a black color source with the additive blend mode!"; + +uniform float roughness< + string label = "Roughness"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.25; + +uniform float lightStrength< + string label = "lightStrength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.001; +> = 0.5; + +uniform float LightPositionX< + string label = "Light Position X"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float LightPositionY< + string label = "Light Position Y"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float flattenNormal< + string label = "Flatten Normal"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.1; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float stretchNormalX< + string label = "Stretch Normal X"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 4.0; + float step = 0.01; +> = 1; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float stretchNormalY< + string label = "Stretch Normal Y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 4.0; + float step = 0.01; +> = 1; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float3 Light_Color = {1,1,1}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +float Square(float a) { return a * a; } +float CookTorrance(float3 lightDir, float3 normal, float roughness) { + float3 h = normalize(lightDir + float3(0,0,1)); + float nh2 = Square(saturate(dot(normal, h))); + float lh2 = Square(saturate(dot(lightDir, h))); + float r2 = Square(roughness); + float d2 = Square(nh2 * (r2 - 1.0) + 1.00001); + float normalization = roughness * 4.0 + 2.0; + return r2 / (d2 * max(0.1, lh2) * normalization); } +#define PI 3.14159265 -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentProgramScene { +float4 mainImage(VertData v_in) : TARGET +{ + float4 c0 = image.Sample(textureSampler, v_in.uv); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentProgramScene')] -[Alias('obs.powershell.websocket.SetCurrentProgramScene')] -param( + float3 lightDir = normalize(float3(-LightPositionX*5, -LightPositionY*5, 1)); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + float2 normalUV = v_in.uv - 0.5; + normalUV.x /= stretchNormalX; + normalUV.y /= stretchNormalY; + normalUV += 0.5; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float3 normal = normalize(float3(normalUV.x * 2 - 1,normalUV.y * 2 - 1,-1)); + normal.z *= -1; + normal = lerp(normal, float3(0,0,-1), flattenNormal); -process { + float3 light = CookTorrance(lightDir, normal, roughness)*float3(1,1,1)*lightStrength*Light_Color; + return float4(c0 + light,c0.a); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -59973,212 +58164,219 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneCollection { - +function Get-OBSSpotlightShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneCollection')] -[Alias('obs.powershell.websocket.SetCurrentSceneCollection')] +[Alias('Set-OBSSpotlightShader','Add-OBSSpotlightShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneCollectionName')] -[string] -$SceneCollectionName, -# If set, will return the information that would otherwise be sent to OBS. +# Set the Speed_Percent of OBSSpotlightShader +[Alias('Speed_Percent')] +[ComponentModel.DefaultBindingProperty('Speed_Percent')] +[Single] +$SpeedPercent, +# Set the Focus_Percent of OBSSpotlightShader +[Alias('Focus_Percent')] +[ComponentModel.DefaultBindingProperty('Focus_Percent')] +[Single] +$FocusPercent, +# Set the Glitch of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the Spotlight_Color of OBSSpotlightShader +[Alias('Spotlight_Color')] +[ComponentModel.DefaultBindingProperty('Spotlight_Color')] +[String] +$SpotlightColor, +# Set the Horizontal_Offset of OBSSpotlightShader +[Alias('Horizontal_Offset')] +[ComponentModel.DefaultBindingProperty('Horizontal_Offset')] +[Single] +$HorizontalOffset, +# Set the Vertical_Offset of OBSSpotlightShader +[Alias('Vertical_Offset')] +[ComponentModel.DefaultBindingProperty('Vertical_Offset')] +[Single] +$VerticalOffset, +# Set the Notes of OBSSpotlightShader +[ComponentModel.DefaultBindingProperty('Notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'spotlight' +$ShaderNoun = 'OBSSpotlightShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Spotlight By Charles Fettinger (https://github.com/Oncorporation) 4/2019 +uniform float Speed_Percent< + string label = "Speed Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 100.0; +uniform float Focus_Percent< + string label = "Focus Percent"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 0.01; +> = 15.0; +uniform bool Glitch; +uniform float4 Spotlight_Color; +uniform float Horizontal_Offset< + string label = "Horizontal Offset"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = 0.0; +uniform float Vertical_Offset< + string label = "Vertical Offset"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.001; +> = -0.5; +uniform string Notes< + string widget_type = "info"; +> = "use negative Focus Percent to create a shade effect, speed zero is a stationary spotlight"; +float4 mainImage(VertData v_in) : TARGET +{ + float speed = Speed_Percent * 0.01; + float focus = Focus_Percent; + if (Glitch) + { + speed *= ((rand_f * 2) - 1) * 0.01; + focus *= ((rand_f * 1.1) - 0.1); + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample( textureSampler, v_in.uv); + float3 lightsrc = float3(sin(elapsed_time * speed * PI * 0.667) *.5 + .5 + Horizontal_Offset, cos(elapsed_time * speed * PI) *.5 + .5 + Vertical_Offset, 1); + float3 light = normalize(lightsrc - float3( v_in.uv.x + (Horizontal_Offset * speed), v_in.uv.y + (Vertical_Offset * speed), 0)); + c0 *= pow(dot(light, float3(0, 0, 1)), focus) * Spotlight_Color; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + return c0; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransition { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransition')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransition')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -60187,218 +58385,238 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionDuration { - +function Get-OBSSwirlShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionDuration')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionDuration')] +[Alias('Set-OBSSwirlShader','Add-OBSSwirlShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. +# Set the radius of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the angle of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the center_x of OBSSwirlShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSSwirlShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the animate of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('animate')] +[Management.Automation.SwitchParameter] +$Animate, +# Set the inverse of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('inverse')] +[Management.Automation.SwitchParameter] +$Inverse, +# Set the notes of OBSSwirlShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Swirl' +$ShaderNoun = 'OBSSwirlShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "Radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; // +uniform float angle< + string label = "Angle"; + string widget_type = "slider"; + float minimum = -360.0; + float maximum = 360.0; + float step = 0.01; +> = 270.0; // +uniform float center_x< + string label = "Center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // +uniform float center_y< + string label = "Center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; // - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform bool animate = false; +uniform bool inverse = false; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform string notes< + string widget_type = "info"; +> = "Distorts the screen, twisting the image in a circular motion." - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + v_out.uv = 1. * v_in.uv - center; - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + center.x /= ar; + v_out.uv.x /= ar; + + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float percent = (radius-dist)/(radius); + percent = inverse == false ? percent : 1 - percent; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + float theta = percent * percent * radians(angle * (animate == true ? sin(elapsed_time) : 1.0)); + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv ); + } + } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSCurrentSceneTransitionSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetCurrentSceneTransitionSettings')] -[Alias('obs.powershell.websocket.SetCurrentSceneTransitionSettings')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionSettings')] -[PSObject] -$TransitionSettings, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -60407,233 +58625,400 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioBalance { - +function Get-OBSTetraShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioBalance')] -[Alias('obs.powershell.websocket.SetInputAudioBalance')] +[Alias('Set-OBSTetraShader','Add-OBSTetraShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the redR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redR')] +[Single] +$RedR, +# Set the redG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redG')] +[Single] +$RedG, +# Set the redB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('redB')] +[Single] +$RedB, +# Set the yelR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelR')] +[Single] +$YelR, +# Set the yelG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelG')] +[Single] +$YelG, +# Set the yelB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('yelB')] +[Single] +$YelB, +# Set the grnR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnR')] +[Single] +$GrnR, +# Set the grnG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnG')] +[Single] +$GrnG, +# Set the grnB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('grnB')] +[Single] +$GrnB, +# Set the cynR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynR')] +[Single] +$CynR, +# Set the cynG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynG')] +[Single] +$CynG, +# Set the cynB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('cynB')] +[Single] +$CynB, +# Set the bluR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluR')] +[Single] +$BluR, +# Set the bluG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluG')] +[Single] +$BluG, +# Set the bluB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('bluB')] +[Single] +$BluB, +# Set the magR of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magR')] +[Single] +$MagR, +# Set the magG of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magG')] +[Single] +$MagG, +# Set the magB of OBSTetraShader +[ComponentModel.DefaultBindingProperty('magB')] +[Single] +$MagB, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioBalance')] -[ValidateRange(0,1)] -[double] -$InputAudioBalance, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'tetra' +$ShaderNoun = 'OBSTetraShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Tetrahedral Interpolation Shader for OBS +uniform float redR< + string label = "Red in Red"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +uniform float redG< + string label = "Green in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +uniform float redB< + string label = "Blue in Red"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +uniform float yelR< + string label = "Red in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform float yelG< + string label = "Green in Yellow"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform float yelB< + string label = "Blue in Yellow"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -} +uniform float grnR< + string label = "Red in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float grnG< + string label = "Green in Green"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -} +uniform float grnB< + string label = "Blue in Green"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioMonitorType { +uniform float cynR< + string label = "Red in Cyan"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float cynG< + string label = "Green in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioMonitorType')] -[Alias('obs.powershell.websocket.SetInputAudioMonitorType')] -param( +uniform float cynB< + string label = "Blue in Cyan"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +uniform float bluR< + string label = "Red in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, +uniform float bluG< + string label = "Green in Blue"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('monitorType')] -[string] -$MonitorType, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform float bluB< + string label = "Blue in Blue"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; +uniform float magR< + string label = "Red in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; -process { +uniform float magG< + string label = "Green in Magenta"; + string widget_type = "slider"; + float minimum = -1.0; + float maximum = 1.0; + float step = 0.01; +> = 0.0; +uniform float magB< + string label = "Blue in Magenta"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 2.0; + float step = 0.01; +> = 1.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +float3 tetra(float3 RGBimage, float3 red, float3 yel, float3 grn, float3 cyn, float3 blu, float3 mag) { + float r = RGBimage.x; + float g = RGBimage.y; + float b = RGBimage.z; + + float3 wht = float3(1.0, 1.0, 1.0); + + if (r > g) { + if (g > b) { + // r > g > b + return r * red + g * (yel - red) + b * (wht - yel); + } else if (r > b) { + // r > b > g + return r * red + g * (wht - mag) + b * (mag - red); + } else { + // b > r > g + return r * (mag - blu) + g * (wht - mag) + b * blu; + } + } else { + if (b > g) { + // b > g > r + return r * (wht - cyn) + g * (cyn - blu) + b * blu; + } else if (b > r) { + // g > b > r + return r * (wht - cyn) + g * grn + b * (cyn - grn); + } else { + // g > r > b + return r * (yel - grn) + g * grn + b * (wht - yel); } + } +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 inputColor = image.Sample(textureSampler, v_in.uv); + float alpha = inputColor.a; + + float3 red = float3(redR, redG, redB); + float3 yel = float3(yelR, yelG, yelB); + float3 grn = float3(grnR, grnG, grnB); + float3 cyn = float3(cynR, cynG, cynB); + float3 blu = float3(bluR, bluG, bluB); + float3 mag = float3(magR, magG, magB); + + float3 outputColor = tetra(inputColor.rgb, red, yel, grn, cyn, blu, mag); + return float4(outputColor, alpha); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -60642,116 +59027,176 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioSyncOffset { - +function Get-OBSThermalShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioSyncOffset')] -[Alias('obs.powershell.websocket.SetInputAudioSyncOffset')] +[Alias('Set-OBSThermalShader','Add-OBSThermalShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the strength of OBSThermalShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioSyncOffset')] -[ValidateRange(-950,20000)] -[double] -$InputAudioSyncOffset, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'thermal' +$ShaderNoun = 'OBSThermalShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/mdKXzG +uniform float strength< + string label = "Strength"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 200.0; + float step = 0.1; +> = 100.0; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float greyScale(float3 c) { + return 0.29 * c.r + 0.60 * c.g + 0.11; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float3 heatMap(float greyValue) { + float3 heat; + heat.r = smoothstep(0.5, 0.8, greyValue); + if(greyValue >= 0.8333) { + heat.r *= (1.1 - greyValue) * 5.0; + } + if(greyValue > 0.6) { + heat.g = smoothstep(1.0, 0.7, greyValue); + } else { + heat.g = smoothstep(0.0, 0.7, greyValue); + } + heat.b = smoothstep(1.0, 0.0, greyValue); + if(greyValue <= 0.3333) { + heat.b *= greyValue / 0.3; + } + return heat; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +float4 mainImage(VertData v_in) : TARGET +{ + float4 c = image.Sample(textureSampler, v_in.uv); + float greyValue = greyScale(c.rgb); + float3 h = heatMap(greyValue*(strength/100.0)); + return float4(h.r, h.g, h.b, c.a); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -60760,232 +59205,219 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputAudioTracks { - +function Get-OBSTvCrtSubpixelShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputAudioTracks')] -[Alias('obs.powershell.websocket.SetInputAudioTracks')] +[Alias('Set-OBSTvCrtSubpixelShader','Add-OBSTvCrtSubpixelShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the channelWidth of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelWidth')] +[Int32] +$ChannelWidth, +# Set the channelHeight of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('channelHeight')] +[Int32] +$ChannelHeight, +# Set the hGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('hGap')] +[Int32] +$HGap, +# Set the vGap of OBSTvCrtSubpixelShader +[ComponentModel.DefaultBindingProperty('vGap')] +[Int32] +$VGap, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputAudioTracks')] -[PSObject] -$InputAudioTracks, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'tv-crt-subpixel' +$ShaderNoun = 'OBSTvCrtSubpixelShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/dlBBz1 adopted for OBS by Exeldro +// width of a single color channel in pixels +uniform int channelWidth< + string label = "Channel Width"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputMute { - +// height of color channels in pixels +uniform int channelHeight< + string label = "Channel Height"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 3; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputMute')] -[Alias('obs.powershell.websocket.SetInputMute')] -param( +// horizontal distance between two neighboring pixels +uniform int hGap< + string label = "Horizontal Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, +// vertical distance between two neighboring pixels +uniform int vGap< + string label = "Vertical Gap"; + string widget_type = "slider"; + int minimum = 1; + int maximum = 20; + int step = 1; +> = 1; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, +float4 mainImage(VertData v_in) : TARGET +{ + float columns = float(channelWidth * 3 + hGap); + float pixelHeight = float(channelHeight + vGap); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputMuted')] -[switch] -$InputMuted, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float2 fragCoord = v_in.uv * uv_size; + float2 sampleRes = float2(uv_size.x / columns, uv_size.y / pixelHeight); + float2 pixel = float2(floor(fragCoord.x / columns), floor(fragCoord.y / pixelHeight)); + float2 sampleUv = pixel / sampleRes; + // color of sample point + float4 col = image.Sample(textureSampler, sampleUv); + + int column = int(fragCoord.x) % (channelWidth * 3 + hGap); -process { + // set color based on which channel this fragment corresponds to + if (column < channelWidth * 1) col = float4(col.r, 0.0, 0.0, col.a); + else if (column < channelWidth * 2) col = float4(0.0, col.g, 0.0, col.a); + else if (column < channelWidth * 3) col = float4(0.0, 0.0, col.b, col.a); + else col = float4(0.0, 0.0, 0.0, col.a); + // offset every other column of pixels + int height = int(pixelHeight); + if (int(pixel.x) % 2 == 0) { + if (int(fragCoord.y) % height >= height - vGap) col = float4(0.0, 0.0, 0.0, col.a); + } else { + if (int(fragCoord.y) % height < vGap) col = float4(0.0, 0.0, 0.0, col.a); + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + // Output to screen + return col; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -60994,115 +59426,202 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputName { - +function Get-OBSTwistShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputName')] -[Alias('obs.powershell.websocket.SetInputName')] +[Alias('Set-OBSTwistShader','Add-OBSTwistShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the center_x_percent of OBSTwistShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSTwistShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the power of OBSTwistShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# Set the rotation of OBSTwistShader +[ComponentModel.DefaultBindingProperty('rotation')] +[Single] +$Rotation, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newInputName')] -[string] -$NewInputName, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'twist' +$ShaderNoun = 'OBSTwistShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int center_x_percent< + string label = "center x percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percentage"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.3; +uniform float rotation< + string label = "rotation"; + string widget_type = "slider"; + float minimum = -100.0; + float maximum = 100.0; + float step = 0.001; +> = 2.0; +#ifndef OPENGL +#define mat2 float2x2 +#endif - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +mat2 rotate(float angle){ + return mat2(float2(cos(angle), -sin(angle)), float2(sin(angle), cos(angle))); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float d = distance(center_pos,v_in.uv); + if(d > power){ + return image.Sample(textureSampler, v_in.uv); + } + float r = (cos(d*3.14159265359/power) +1)/2 * rotation; + float2 pos = v_in.uv - center_pos; + pos = mul(pos, rotate(r)); + pos += center_pos; + return image.Sample(textureSampler, pos); +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -61111,244 +59630,314 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputSettings { - +function Get-OBSTwoPassDropShadowShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputSettings')] -[Alias('obs.powershell.websocket.SetInputSettings')] +[Alias('Set-OBSTwoPassDropShadowShader','Add-OBSTwoPassDropShadowShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputSettings')] -[PSObject] -$InputSettings, - +# Set the ViewProj of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('ViewProj')] +[Single[][]] +$ViewProj, +# Set the image of OBSTwoPassDropShadowShader +[ComponentModel.DefaultBindingProperty('image')] +[String] +$Image, +# Set the elapsed_time of OBSTwoPassDropShadowShader +[Alias('elapsed_time')] +[ComponentModel.DefaultBindingProperty('elapsed_time')] +[Single] +$ElapsedTime, +# Set the uv_offset of OBSTwoPassDropShadowShader +[Alias('uv_offset')] +[ComponentModel.DefaultBindingProperty('uv_offset')] +[Single[]] +$UvOffset, +# Set the uv_scale of OBSTwoPassDropShadowShader +[Alias('uv_scale')] +[ComponentModel.DefaultBindingProperty('uv_scale')] +[Single[]] +$UvScale, +# Set the uv_pixel_interval of OBSTwoPassDropShadowShader +[Alias('uv_pixel_interval')] +[ComponentModel.DefaultBindingProperty('uv_pixel_interval')] +[Single[]] +$UvPixelInterval, +# Set the rand_f of OBSTwoPassDropShadowShader +[Alias('rand_f')] +[ComponentModel.DefaultBindingProperty('rand_f')] +[Single] +$RandF, +# Set the uv_size of OBSTwoPassDropShadowShader +[Alias('uv_size')] +[ComponentModel.DefaultBindingProperty('uv_size')] +[Single[]] +$UvSize, +# Set the shadow_offset_x of OBSTwoPassDropShadowShader +[Alias('shadow_offset_x')] +[ComponentModel.DefaultBindingProperty('shadow_offset_x')] +[Int32] +$ShadowOffsetX, +# Set the shadow_offset_y of OBSTwoPassDropShadowShader +[Alias('shadow_offset_y')] +[ComponentModel.DefaultBindingProperty('shadow_offset_y')] +[Int32] +$ShadowOffsetY, +# Set the shadow_blur_size of OBSTwoPassDropShadowShader +[Alias('shadow_blur_size')] +[ComponentModel.DefaultBindingProperty('shadow_blur_size')] +[Int32] +$ShadowBlurSize, +# Set the shadow_color of OBSTwoPassDropShadowShader +[Alias('shadow_color')] +[ComponentModel.DefaultBindingProperty('shadow_color')] +[String] +$ShadowColor, +# Set the is_alpha_premultiplied of OBSTwoPassDropShadowShader +[Alias('is_alpha_premultiplied')] +[ComponentModel.DefaultBindingProperty('is_alpha_premultiplied')] +[Management.Automation.SwitchParameter] +$IsAlphaPremultiplied, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'two-pass-drop-shadow' +$ShaderNoun = 'OBSTwoPassDropShadowShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 22, 2022 +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float elapsed_time; +uniform float2 uv_offset; +uniform float2 uv_scale; +uniform float2 uv_pixel_interval; +uniform float rand_f; +uniform float2 uv_size; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +sampler_state textureSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 00000000; +}; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; +VertData mainTransform(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv * uv_scale + uv_offset; + return vert_out; } +uniform int shadow_offset_x< + string label = "shadow offset x"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_offset_y< + string label = "shadow offset y"; + string widget_type = "slider"; + int minimum = -1000; + int maximum = 1000; + int step = 1; +>; +uniform int shadow_blur_size< + string label = "shadow blur size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +>; -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSInputVolume { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetInputVolume')] -[Alias('obs.powershell.websocket.SetInputVolume')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeMul')] -[ValidateRange(0,20)] -[double] -$InputVolumeMul, +uniform float4 shadow_color; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputVolumeDb')] -[ValidateRange(-100,26)] -[double] -$InputVolumeDb, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform bool is_alpha_premultiplied; +float4 mainImage(VertData v_in) : TARGET +{ + int shadow_blur_samples = int(shadow_blur_size + 1);//pow(shadow_blur_size * 2 + 1, 2); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * int(shadow_offset_x), + v_in.uv.y - uv_pixel_interval.y * int(shadow_offset_y)); + + float sampled_shadow_alpha = 0; + + for (int blur_x = -shadow_blur_size; blur_x <= shadow_blur_size; blur_x++) + { + float2 blur_uv = shadow_uv + float2(uv_pixel_interval.x * blur_x, 0); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; + } + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); +} -process { +float4 mainImage_2_end(VertData v_in) : TARGET +{ + int shadow_blur_samples = shadow_blur_size + 1;//pow(shadow_blur_size * 2 + 1, 2); + + float4 color = image.Sample(textureSampler, v_in.uv); + float2 shadow_uv = float2(v_in.uv.x - uv_pixel_interval.x * shadow_offset_x, + v_in.uv.y - uv_pixel_interval.y * shadow_offset_y); + + float sampled_shadow_alpha = 0; + + for (int blur_y = -shadow_blur_size; blur_y <= shadow_blur_size; blur_y++) + { + float2 blur_uv = shadow_uv + float2(0, uv_pixel_interval.y * blur_y); + sampled_shadow_alpha += image.Sample(textureSampler, blur_uv).a; + } + + sampled_shadow_alpha /= shadow_blur_samples; + + float4 final_shadow_color = float4(shadow_color.rgb, shadow_color.a * sampled_shadow_alpha); + + return final_shadow_color * (1-color.a) + color * (is_alpha_premultiplied?1.0:color.a); +} +technique Draw +{ + pass p0 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } + + pass p1 + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage_2_end(v_in); + } +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -61357,228 +59946,260 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSMediaInputCursor { - +function Get-OBSVCRShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetMediaInputCursor')] -[Alias('obs.powershell.websocket.SetMediaInputCursor')] +[Alias('Set-OBSVCRShader','Add-OBSVCRShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, - +# Set the vertical_shift of OBSVCRShader +[Alias('vertical_shift')] +[ComponentModel.DefaultBindingProperty('vertical_shift')] +[Single] +$VerticalShift, +# Set the distort of OBSVCRShader +[ComponentModel.DefaultBindingProperty('distort')] +[Single] +$Distort, +# Set the vignet of OBSVCRShader +[ComponentModel.DefaultBindingProperty('vignet')] +[Single] +$Vignet, +# Set the stripe of OBSVCRShader +[ComponentModel.DefaultBindingProperty('stripe')] +[Single] +$Stripe, +# Set the vertical_factor of OBSVCRShader +[Alias('vertical_factor')] +[ComponentModel.DefaultBindingProperty('vertical_factor')] +[Single] +$VerticalFactor, +# Set the vertical_height of OBSVCRShader +[Alias('vertical_height')] +[ComponentModel.DefaultBindingProperty('vertical_height')] +[Single] +$VerticalHeight, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('mediaCursor')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$MediaCursor, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) -process { +process { +$shaderName = 'VCR' +$ShaderNoun = 'OBSVCRShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/ldjGzV +//Converted to OpenGL by Exeldro February 19, 2022 +uniform float vertical_shift< + string label = "vertical shift"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 0.4; +uniform float distort< + string label = "distort"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 5.0; + float step = 0.001; +> = 1.2; +uniform float vignet< + string label = "vignet"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float stripe< + string label = "stripe"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_factor< + string label = "vertical factor"; + string widget_type = "slider"; + float minimum = -5.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform float vertical_height< + string label = "vertical height"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1000.0; + float step = 0.1; +> = 30.0; + +float onOff(float a, float b, float c) +{ + return step(c, sin(elapsed_time + a*cos(elapsed_time*b))); +} + +float ramp(float y, float start, float end) +{ + float inside = step(start,y) - step(end,y); + float fact = (y-start)/(end-start)*inside; + return (1.-fact) * inside; + +} + +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} +float stripes(float2 uv) +{ + return ramp(modu(uv.y*4. + elapsed_time/2.+sin(elapsed_time + sin(elapsed_time*0.63)),1.),0.5,0.6)*stripe; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 getVideo(float2 uv) +{ + float2 look = uv; + float window = 1./(1.+20.*(look.y-modu(elapsed_time/4.,1.))*(look.y-modu(elapsed_time/4.,1.))); + look.x = look.x + sin(look.y*10. + elapsed_time)/50.*onOff(4.,4.,.3)*(1.+cos(elapsed_time*80.))*window; + float vShift = vertical_shift*onOff(2.,3.,.9)*(sin(elapsed_time)*sin(elapsed_time*20.) + + (0.5 + 0.1*sin(elapsed_time*200.)*cos(elapsed_time))); + look.y = modu((look.y + vShift) , 1.); + return image.Sample(textureSampler, look); +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } +float2 screenDistort(float2 uv) +{ + uv -= float2(.5,.5); + uv = uv*distort*(1./1.2+2.*uv.x*uv.x*uv.y*uv.y); + uv += float2(.5,.5); + return uv; +} - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + uv = screenDistort(uv); + float4 video = getVideo(uv); + float vigAmt = 3.+.3*sin(elapsed_time + 5.*cos(elapsed_time*5.)); + float vignette = ((1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5))-1.)*vignet+1.; + video += stripes(uv); + video *= vignette; + video *= (((12.+modu((uv.y*vertical_height+elapsed_time),1.))/13.)-1.)*vertical_factor+1.; + return float4(video.r, video.g, video.b ,1.0); +} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSOutputSettings { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetOutputSettings')] -[Alias('obs.powershell.websocket.SetOutputSettings')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputSettings')] -[PSObject] -$OutputSettings, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -61587,232 +60208,306 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSPersistentData { - +function Get-OBSVHSShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetPersistentData')] -[Alias('obs.powershell.websocket.SetPersistentData')] +[Alias('Set-OBSVHSShader','Add-OBSVHSShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('realm')] -[string] -$Realm, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotName')] -[string] -$SlotName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('slotValue')] -[PSObject] -$SlotValue, -# If set, will return the information that would otherwise be sent to OBS. +# Set the range of OBSVHSShader +[ComponentModel.DefaultBindingProperty('range')] +[Single] +$Range, +# Set the offsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('offsetIntensity')] +[Single] +$OffsetIntensity, +# Set the noiseQuality of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseQuality')] +[Single] +$NoiseQuality, +# Set the noiseIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('noiseIntensity')] +[Single] +$NoiseIntensity, +# Set the colorOffsetIntensity of OBSVHSShader +[ComponentModel.DefaultBindingProperty('colorOffsetIntensity')] +[Single] +$ColorOffsetIntensity, +# Set the Alpha_Percentage of OBSVHSShader +[Alias('Alpha_Percentage')] +[ComponentModel.DefaultBindingProperty('Alpha_Percentage')] +[Single] +$AlphaPercentage, +# Set the Apply_To_Image of OBSVHSShader +[Alias('Apply_To_Image')] +[ComponentModel.DefaultBindingProperty('Apply_To_Image')] +[Management.Automation.SwitchParameter] +$ApplyToImage, +# Set the Replace_Image_Color of OBSVHSShader +[Alias('Replace_Image_Color')] +[ComponentModel.DefaultBindingProperty('Replace_Image_Color')] +[Management.Automation.SwitchParameter] +$ReplaceImageColor, +# Set the Color_To_Replace of OBSVHSShader +[Alias('Color_To_Replace')] +[ComponentModel.DefaultBindingProperty('Color_To_Replace')] +[String] +$ColorToReplace, +# Set the Apply_To_Specific_Color of OBSVHSShader +[Alias('Apply_To_Specific_Color')] +[ComponentModel.DefaultBindingProperty('Apply_To_Specific_Color')] +[Management.Automation.SwitchParameter] +$ApplyToSpecificColor, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'VHS' +$ShaderNoun = 'OBSVHSShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/Ms3XWH converted by Exeldro v 1.0 +//updated by Charles ''Surn'' Fettinger for obs-shaderfilter 9/2020 +//Converted to OpenGL by Exeldro February 19, 2022 +//Use improved input fields by Exeldro April 15, 2023 +uniform float range< + string label = "Wave size (0.05)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.20; + float step = 0.01; +> = 0.05; +uniform float offsetIntensity< + string label = "Offset intensity (0.02)"; + string widget_type = "slider"; + float minimum = 0.01; + float maximum = 0.20; + float step = 0.01; +> = 0.02; +uniform float noiseQuality< + string label = "Noise number of lines (250)"; + string widget_type = "slider"; + float minimum = 1.0; + float maximum = 1000.0; + float step = 10.0; +> = 250.0; +uniform float noiseIntensity< + string label = "Noise intensity (0.88)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.01; +> = 0.88; +uniform float colorOffsetIntensity< + string label = "Color offset intensity (1.3)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 10.0; + float step = 0.1; +> = 1.3; +uniform float Alpha_Percentage< + string label = "Aplha percentage (100.0)"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 100.0; + float step = 1.0; +> = 100.0; +uniform bool Apply_To_Image; +uniform bool Replace_Image_Color; +uniform float4 Color_To_Replace; +uniform bool Apply_To_Specific_Color; - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float dot2(float2 a,float2 b){ + return a.x*b.x+a.y*b.y; } +float rand(float2 co) +{ + return frac(sin(dot2(co.xy ,float2(12.9898,78.233))) * 43758.5453); +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSProfileParameter { +float verticalBar(float pos, float uvY, float offset) +{ + float edge0 = (pos - range); + float edge1 = (pos + range); + float x = smoothstep(edge0, pos, uvY) * offset; + x -= smoothstep(pos, edge1, uvY) * offset; + return x; +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetProfileParameter')] -[Alias('obs.powershell.websocket.SetProfileParameter')] -param( +float modu(float x, float y) +{ + return (x / y) - floor(x / y); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterCategory')] -[string] -$ParameterCategory, +float dot4(float4 a,float4 b){ + return a.r*b.r+a.g*b.g+a.b*b.b+a.a*b.a; +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterName')] -[string] -$ParameterName, +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + for (float i = 0.0; i < 0.71; i += 0.1313) + { + float d = modu(elapsed_time * i, 1.7); + float o = sin(1.0 - tan(elapsed_time * 0.24 * i)); + o *= offsetIntensity; + uv.x += verticalBar(d, uv.y, o); + } + float uvY = uv.y; + uvY *= noiseQuality; + uvY = float(int(uvY)) * (1.0 / noiseQuality); + float noise = rand(float2(elapsed_time * 0.00001, uvY)); + uv.x += noise * noiseIntensity / 100.0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('parameterValue')] -[string] -$ParameterValue, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + float2 offsetR = float2(0.006 * sin(elapsed_time), 0.0) * colorOffsetIntensity; + float2 offsetG = float2(0.0073 * (cos(elapsed_time * 0.97)), 0.0) * colorOffsetIntensity; + float4 rgba = image.Sample(textureSampler, uv); + float r = image.Sample(textureSampler, uv + offsetR).r; + float g = image.Sample(textureSampler, uv + offsetG).g; + float b = rgba.b; -process { + rgba = float4(r, g, b, rgba.a); + + float4 color; + float4 original_color; + if (Apply_To_Image) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + float luma = dot4(color, float4(0.30, 0.59, 0.11, 1.0)); + if (Replace_Image_Color) + color = float4(luma,luma,luma,luma); + rgba = lerp(original_color, rgba * color, clamp(Alpha_Percentage * .01, 0, 1.0)); + + } + if (Apply_To_Specific_Color) + { + color = image.Sample(textureSampler, v_in.uv); + original_color = color; + color = (distance(color.rgb, Color_To_Replace.rgb) <= 0.075) ? rgba : color; + rgba = lerp(original_color, color, clamp(Alpha_Percentage * .01, 0, 1.0)); + } + return rgba; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -61821,228 +60516,433 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSRecordDirectory { - +function Get-OBSVignettingShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetRecordDirectory')] -[Alias('obs.powershell.websocket.SetRecordDirectory')] +[Alias('Set-OBSVignettingShader','Add-OBSVignettingShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('recordDirectory')] -[string] -$RecordDirectory, -# If set, will return the information that would otherwise be sent to OBS. +# Set the innerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('innerRadius')] +[Single] +$InnerRadius, +# Set the outerRadius of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('outerRadius')] +[Single] +$OuterRadius, +# Set the opacity of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('opacity')] +[Single] +$Opacity, +# Set the notes of OBSVignettingShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'vignetting' +$ShaderNoun = 'OBSVignettingShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Converted to OpenGL by Q-mii & Exeldro February 21, 2022 +uniform float innerRadius< + string label = "inner radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.9; +uniform float outerRadius< + string label = "outer radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.5; +uniform float opacity< + string label = "opacity"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.8; +uniform string notes< + string widget_type = "info"; +> = "inner radius will always be shown, outer radius is the falloff"; +float4 mainImage(VertData v_in) : TARGET +{ + float PI = 3.1415926535897932384626433832795;//acos(-1); - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float4 c0 = image.Sample(textureSampler, v_in.uv); + float verticalDim = 0.5 + sin (v_in.uv.y * PI) * 0.9 ; + + float xTrans = (v_in.uv.x * 2) - 1; + float yTrans = 1 - (v_in.uv.y * 2); + + float radius = sqrt(pow(xTrans, 2) + pow(yTrans, 2)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float subtraction = max(0, radius - innerRadius) / max((outerRadius - innerRadius), 0.01); + float factor = 1 - subtraction; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + float4 vignetColor = c0 * factor; + vignetColor *= verticalDim; + + vignetColor *= opacity; + c0 *= 1-opacity; + + float4 output_color = c0 + vignetColor; + + return float4(output_color); +} + +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } + continue nextParameter + } + } + + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } -} +} + + +#.ExternalHelp obs-powershell-Help.xml +function Get-OBSVoronoiPixelationShader { + +[Alias('Set-OBSVoronoiPixelationShader','Add-OBSVoronoiPixelationShader')] +param( +# Set the pixH of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('pixH')] +[Single] +$PixH, +# Set the alternative of OBSVoronoiPixelationShader +[ComponentModel.DefaultBindingProperty('alternative')] +[Management.Automation.SwitchParameter] +$Alternative, +# The name of the source. This must be provided when adding an item for the first time +[Parameter(ValueFromPipelineByPropertyName)] +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. +[Parameter(ValueFromPipelineByPropertyName)] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'voronoi-pixelation' +$ShaderNoun = 'OBSVoronoiPixelationShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// https://www.shadertoy.com/view/sd3yzn adopted by Exeldro - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemBlendMode { +uniform float pixH< + string label = "Size"; + string widget_type = "slider"; + float minimum = 4.0; + float maximum = 500.0; + float step = 0.01; +> = 100.0; +uniform bool alternative; +float2 fract2(float2 v){ + return float2(v.x - floor(v.x), v.y - floor(v.y)); +} -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemBlendMode')] -[Alias('obs.powershell.websocket.SetSceneItemBlendMode')] -param( +float2 random2( float2 p ) { + return fract2(sin(float2(dot(p,float2(127.1,311.7)),dot(p,float2(269.5,183.3))))*43758.5453); +} +float2 randomSpin(float2 p, float f){ + return 1.0 * float2( + cos( f * elapsed_time * 3.14159 * sign(random2(p).y - 0.5) + random2(p).y * 3.14159), + sin( f * elapsed_time * 3.14159 * sign(random2(p).x - 0.5) + random2(p).x * 3.14159)); +} +float4 VoronoiPixelation(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, + float2 point1 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point1 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; + } + } + } + float2 nPoint = pixExt + relClos + randomSpin(pixExt + relClos, 0.5) + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x ; + + return image.Sample(textureSampler, nPoint); +} +float4 VoronoiPixelation2(float2 uv, float pixH ){ + float2 pixInt = fract2(uv * pixH); + float2 pixExt = floor(uv * pixH); + float m_dist = 10.0; + float2 relClos = float2(0.0, 0.0); + float2 relRot = 0.5 * float2(cos(elapsed_time), sin(elapsed_time)); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemBlendMode')] -[string] -$SceneItemBlendMode, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + for (int y= -3; y <= 3; y++) { + for (int x= -3; x <= 3; x++) { + float2 neighbor = float2(float(x),float(y)); -process { + float2 point2 = random2(pixExt + neighbor); + float2 relRot = randomSpin(pixExt + neighbor, 0.5); + float2 diff = neighbor + relRot + point2 - pixInt; + float dist = length(diff); + if(dist < m_dist){ + m_dist = dist; + relClos = neighbor; + } + } + } + float2 nPoint = pixExt + relClos + random2(pixExt + relClos); + nPoint = nPoint / pixH; + nPoint.x = nPoint.x * uv_scale.x; + + return image.Sample(textureSampler, nPoint); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +float4 mainImage(VertData v_in) : TARGET +{ + if (alternative) { + return VoronoiPixelation2(v_in.uv, pixH); + } else { + return VoronoiPixelation(v_in.uv, pixH); + } +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -62051,368 +60951,379 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemEnabled { - +function Get-OBSWalkingDeadPixelFixerShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemEnabled')] -[Alias('obs.powershell.websocket.SetSceneItemEnabled')] +[Alias('Set-OBSWalkingDeadPixelFixerShader','Add-OBSWalkingDeadPixelFixerShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +# Set the Scan_Width of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Width')] +[ComponentModel.DefaultBindingProperty('Scan_Width')] +[Int32] +$ScanWidth, +# Set the Scan_Height of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Height')] +[ComponentModel.DefaultBindingProperty('Scan_Height')] +[Int32] +$ScanHeight, +# Set the Scan_Offset_X of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_X')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_X')] +[Int32] +$ScanOffsetX, +# Set the Scan_Offset_Y of OBSWalkingDeadPixelFixerShader +[Alias('Scan_Offset_Y')] +[ComponentModel.DefaultBindingProperty('Scan_Offset_Y')] +[Int32] +$ScanOffsetY, +# Set the Show_Border of OBSWalkingDeadPixelFixerShader +[Alias('Show_Border')] +[ComponentModel.DefaultBindingProperty('Show_Border')] +[Management.Automation.SwitchParameter] +$ShowBorder, +# Set the Contrast_Threshold of OBSWalkingDeadPixelFixerShader +[Alias('Contrast_Threshold')] +[ComponentModel.DefaultBindingProperty('Contrast_Threshold')] +[Single] +$ContrastThreshold, +# Set the Min_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Min_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Min_Cluster_Size')] +[Int32] +$MinClusterSize, +# Set the Max_Cluster_Size of OBSWalkingDeadPixelFixerShader +[Alias('Max_Cluster_Size')] +[ComponentModel.DefaultBindingProperty('Max_Cluster_Size')] +[Int32] +$MaxClusterSize, +# Set the Show_Green of OBSWalkingDeadPixelFixerShader +[Alias('Show_Green')] +[ComponentModel.DefaultBindingProperty('Show_Green')] +[Management.Automation.SwitchParameter] +$ShowGreen, +# Set the Bypass of OBSWalkingDeadPixelFixerShader +[ComponentModel.DefaultBindingProperty('Bypass')] +[Management.Automation.SwitchParameter] +$Bypass, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemEnabled')] -[switch] -$SceneItemEnabled, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'walking-dead-pixel-fixer' +$ShaderNoun = 'OBSWalkingDeadPixelFixerShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Walking Dead Pixel Fixer, Version 0.10, for OBS Shaderfilter +// by Eegee http://github.com/eegee/ +// Based on Dead Pixel Fixer, Version 0.01, for OBS Shaderfilter +// Copyright ©️ 2022 by SkeletonBow +// License: GNU General Public License, version 2 +// Contact info: +// Twitter: +// Twitch: +// +// Description: Intended for use with an input source that has a dead pixel on its sensor such as a webcam. +// The pixels located in the user configured scan area and passing the threshold settings will have its colors +// overridden by taking the average of the colors of the surrounding pixels, effectively hiding the dead pixels. +// +// Changelog: +// 0.01 - Initial release +// 0.10 - Added a pixel scan area and added contrast threshold settings to replace blur size setting. +uniform int Scan_Width< + string label = "Scan area width"; + int minimum = 1; + int maximum = 2560; + int step = 1; +> = 75; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } +uniform int Scan_Height< + string label = "Scan area height"; + int minimum = 1; + int maximum = 1440; + int step = 1; +> = 120; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +uniform int Scan_Offset_X< + string label = "Scan area offset X"; + int minimum = 0; + int maximum = 2560; + int step = 1; +> = 110; -} +uniform int Scan_Offset_Y< + string label = "Scan area offset Y"; + int minimum = 0; + int maximum = 1440; + int step = 1; +> = 20; +uniform bool Show_Border< + string label = "Show scan area border in red"; + string widget_type = "checkbox"; +> = true; -} +uniform float Contrast_Threshold< + string label = "Contrast threshold"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.01; +> = 0.05; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemIndex { +uniform int Min_Cluster_Size< + string label = "Min cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 324; +uniform int Max_Cluster_Size< + string label = "Max cluster size"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 600; + int step = 2; +> = 400; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemIndex')] -[Alias('obs.powershell.websocket.SetSceneItemIndex')] -param( +uniform bool Show_Green< + string label = "Show matches in green"; + string widget_type = "checkbox"; +> = true; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, +uniform bool Bypass< + string label = "Bypass"; + string widget_type = "checkbox"; +> = false; -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, +#define SAMPLE_RADIUS 9 -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemIndex, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float luminance(float3 color) +{ + return dot(color, float3(0.299, 0.587, 0.114)); +} +void sample_average(float2 uv, float2 center_uv, out float3 avgColor, out float avgLuminance, out int contrastCount, float contrastThreshold) +{ + float3 sumColor = float3(0.0, 0.0, 0.0); + float weightSum = 0.0; + contrastCount = 0; -process { + float3 centerColor = image.Sample(textureSampler, uv).rgb; + float centerLum = luminance(centerColor); + for (int y = -SAMPLE_RADIUS; y <= SAMPLE_RADIUS; ++y) + { + for (int x = -SAMPLE_RADIUS; x <= SAMPLE_RADIUS; ++x) + { + if (x == 0 && y == 0) continue; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + float2 offset = float2(x, y) / uv_size; + float2 sample_uv = clamp(uv + offset, float2(0.0, 0.0), float2(1.0, 1.0)); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + // skip central pixel + if (ceil(sample_uv.x * uv_size.x) == ceil(center_uv.x) && + ceil(sample_uv.y * uv_size.y) == ceil(center_uv.y)) + continue; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + float3 sampleColor = image.Sample(textureSampler, sample_uv).rgb; + float lum = luminance(sampleColor); - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + float weight = 1.0; + sumColor += sampleColor * weight; + weightSum += weight; - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (abs(lum - centerLum) >= contrastThreshold) + contrastCount++; } + } + if (weightSum > 0) + { + avgColor = sumColor / weightSum; + } + else + { + avgColor = centerColor; + } + avgLuminance = luminance(avgColor); } +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv = v_in.uv; + float2 pos = v_in.pos.xy; -} + float4 tex = image.Sample(textureSampler, uv); + float3 color = tex.rgb; - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemLocked { + if (!Bypass) + { + int pixX = (int)round(pos.x); + int pixY = (int)round(pos.y); + int borderwidth = 2; -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemLocked')] -[Alias('obs.powershell.websocket.SetSceneItemLocked')] -param( + bool insideScan = + (pixX >= Scan_Offset_X && pixX < Scan_Offset_X + Scan_Width) && + (pixY >= Scan_Offset_Y && pixY < Scan_Offset_Y + Scan_Height); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, + bool borderingScan = + (pixX >= Scan_Offset_X - borderwidth && pixX < Scan_Offset_X + Scan_Width + borderwidth) && + (pixY >= Scan_Offset_Y - borderwidth && pixY < Scan_Offset_Y + Scan_Height + borderwidth); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, + if (insideScan) + { + float3 avgColor; + float avgLum; + int contrastCount; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, + sample_average(uv, pos, avgColor, avgLum, contrastCount, Contrast_Threshold); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemLocked')] -[switch] -$SceneItemLocked, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if (contrastCount < Max_Cluster_Size && contrastCount >= Min_Cluster_Size) + { + color = avgColor; + if (Show_Green) + { + int left = pixX - borderwidth; + int right = pixX + borderwidth; + int top = pixY - borderwidth; + int bottom = pixY + borderwidth; -process { + bool onOutline = + abs(pos.x - left) < borderwidth || abs(pos.x - right) < borderwidth || + abs(pos.y - top) < borderwidth || abs(pos.y - bottom) < borderwidth; + if (onOutline) + return float4(0.0, 1.0, 0.0, 1.0); + } + } + } + else if (Show_Border && borderingScan) + { + return float4(1.0, 0.0, 0.0, 0.5); + } + } + return float4(color, tex.a); +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -62421,238 +61332,283 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneItemTransform { - +function Get-OBSZigZagShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneItemTransform')] -[Alias('obs.powershell.websocket.SetSceneItemTransform')] +[Alias('Set-OBSZigZagShader','Add-OBSZigZagShader')] param( - +# Set the radius of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('radius')] +[Single] +$Radius, +# Set the angle of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('angle')] +[Single] +$Angle, +# Set the period of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('period')] +[Single] +$Period, +# Set the amplitude of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('amplitude')] +[Single] +$Amplitude, +# Set the center_x of OBSZigZagShader +[Alias('center_x')] +[ComponentModel.DefaultBindingProperty('center_x')] +[Single] +$CenterX, +# Set the center_y of OBSZigZagShader +[Alias('center_y')] +[ComponentModel.DefaultBindingProperty('center_y')] +[Single] +$CenterY, +# Set the phase of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('phase')] +[Single] +$Phase, +# Set the animate of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('animate')] +[Int32] +$Animate, +# Set the notes of OBSZigZagShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime +) + + +process { +$shaderName = 'ZigZag' +$ShaderNoun = 'OBSZigZagShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//Created by Radegast Stravinsky for obs-shaderfilter 9/2020 +uniform float radius< + string label = "radius"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.0; +uniform float angle< + string label = "angle"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 360.0; + float step = 0.1; +> = 180.0; +uniform float period< + string label = "period"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 0.5; +uniform float amplitude< + string label = "amplitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; + +uniform float center_x< + string label = "center x"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; +uniform float center_y< + string label = "center y"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 0.5; + float step = 0.001; +> = 0.25; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemId')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$SceneItemId, +uniform float phase< + string label = "phase"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 5.0; + float step = 0.001; +> = 1.0; +uniform int animate< + string label = "animate"; + string widget_type = "select"; + int option_0_value = 0; + string option_0_label = "No"; + int option_1_value = 1; + string option_1_label = "Amplitude"; + int option_2_value = 2; + string option_2_label = "Time"; +> = 0; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneItemTransform')] -[PSObject] -$SceneItemTransform, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +uniform string notes = "Distorts the screen, creating a rippling effect that moves clockwise and anticlockwise." -process { +float4 mainImage(VertData v_in) : TARGET +{ + float2 center = float2(center_x, center_y); + VertData v_out; + v_out.pos = v_in.pos; + float2 hw = uv_size; + float ar = 1. * hw.y/hw.x; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + v_out.uv = 1. * v_in.uv - center; + + center.x /= ar; + v_out.uv.x /= ar; + + float dist = distance(v_out.uv, center); + if (dist < radius) + { + float percent = (radius-dist)/radius; + float theta = percent * percent * + ( + animate == 1 ? + amplitude * sin(elapsed_time) : + amplitude + ) + * sin(percent * percent / period * radians(angle) + (phase + + ( + animate == 2 ? + elapsed_time : + 0 + ))); - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + float s = sin(theta); + float c = cos(theta); + v_out.uv = float2(dot(v_out.uv-center, float2(c,-s)), dot(v_out.uv-center, float2(s,c))); + v_out.uv += (2 * center); + + v_out.uv.x *= ar; - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + return image.Sample(textureSampler, v_out.uv); + } + else + { + return image.Sample(textureSampler, v_in.uv); + } + +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneName { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneName')] -[Alias('obs.powershell.websocket.SetSceneName')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newSceneName')] -[string] -$NewSceneName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -62661,243 +61617,256 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSceneSceneTransitionOverride { - +function Get-OBSZoomBlurShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSceneSceneTransitionOverride')] -[Alias('obs.powershell.websocket.SetSceneSceneTransitionOverride')] +[Alias('Set-OBSZoomBlurShader','Add-OBSZoomBlurShader')] param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneName')] -[string] -$SceneName, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sceneUuid')] -[string] -$SceneUuid, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionName')] -[string] -$TransitionName, - +# Set the samples of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('samples')] +[Int32] +$Samples, +# Set the magnitude of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('magnitude')] +[Single] +$Magnitude, +# Set the speed_percent of OBSZoomBlurShader +[Alias('speed_percent')] +[ComponentModel.DefaultBindingProperty('speed_percent')] +[Int32] +$SpeedPercent, +# Set the ease of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('ease')] +[Management.Automation.SwitchParameter] +$Ease, +# Set the glitch of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('glitch')] +[Management.Automation.SwitchParameter] +$Glitch, +# Set the notes of OBSZoomBlurShader +[ComponentModel.DefaultBindingProperty('notes')] +[String] +$Notes, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('transitionDuration')] -[ValidateRange(50,20000)] -[double] -$TransitionDuration, -# If set, will return the information that would otherwise be sent to OBS. +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'zoom_blur' +$ShaderNoun = 'OBSZoomBlurShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// zoom blur shader by Charles Fettinger for obs-shaderfilter plugin 3/2019 +// https://github.com/Oncorporation/obs-shaderfilter +// https://github.com/dinfinity/mpc-pixel-shaders/blob/master/PS_Zoom%20Blur.hlsl +//for Media Player Classic HC or BE +//Converted to OpenGL by Q-mii & Exeldro February 18, 2022 +uniform int samples < + string label = "Samples"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 32; +uniform float magnitude< + string label = "Magnitude"; + string widget_type = "slider"; + float minimum = 0.0; + float maximum = 1.0; + float step = 0.001; +> = 0.5; +uniform int speed_percent < + string label = "Speed percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 0; +uniform bool ease; +uniform bool glitch; +uniform string notes< + string widget_type = "info"; +> = "Speed Percent above zero will animate the zoom. Keep samples low to save power"; +float EaseInOutCircTimer(float t,float b,float c,float d){ + t /= d/2; + if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b; + t -= 2; + return c/2 * (sqrt(1 - t*t) + 1) + b; +} - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } - +float Styler(float t,float b,float c,float d,bool ease) +{ + if (ease) return EaseInOutCircTimer(t,0,c,d); + return t; } +float4 mainImage(VertData v_in) : TARGET +{ + float speed = speed_percent * 0.01; -} + // circular easing variable + float t = 1.0 + sin(elapsed_time * speed); + float b = 0.0; //start value + float c = 2.0; //change value + float d = 2.0; //duration - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterEnabled { + if (glitch) t = clamp(t + ((rand_f *2) - 1), 0.0,2.0); + b = Styler(t, 0, c, d, ease); + float sample_speed = max(samples * b, 1.0); -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterEnabled')] -[Alias('obs.powershell.websocket.SetSourceFilterEnabled')] -param( + float PI = 3.1415926535897932384626433832795;//acos(-1); + float4 c0 = image.Sample(textureSampler, v_in.uv); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, + float xTrans = (v_in.uv.x*2)-1; + float yTrans = 1-(v_in.uv.y*2); + + float angle = atan(yTrans/xTrans) + PI; + if (sign(xTrans) == 1) { + angle+= PI; + } + float radius = sqrt(pow(xTrans,2) + pow(yTrans,2)); -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, + float2 currentCoord; + float4 accumulatedColor = float4(0,0,0,0); -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor = currentColor; -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterEnabled')] -[switch] -$FilterEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + accumulatedColor = c0/sample_speed; + for(int i = 1; i< sample_speed; i++) { + float currentRadius ; + // Distance to center dependent + currentRadius = max(0,radius - (radius/1000 * i * magnitude * b)); + // Continuous; + // currentRadius = max(0,radius - (0.0004 * i)); -process { + currentCoord.x = (currentRadius * cos(angle)+1.0)/2.0; + currentCoord.y = -1* ((currentRadius * sin(angle)-1.0)/2.0); + float4 currentColor = image.Sample(textureSampler, currentCoord); + accumulatedColor += currentColor/sample_speed; + + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + return accumulatedColor; +} - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] } - } + continue nextParameter + } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName + } + + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath + } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + $ShaderFilterSplat.ShaderText = $shaderText + } + + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat + } else { + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -62906,243 +61875,229 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterIndex { - +function Get-OBSZoomBlurTransitionShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterIndex')] -[Alias('obs.powershell.websocket.SetSourceFilterIndex')] +[Alias('Set-OBSZoomBlurTransitionShader','Add-OBSZoomBlurTransitionShader')] param( - +# Set the image_a of OBSZoomBlurTransitionShader +[Alias('image_a')] +[ComponentModel.DefaultBindingProperty('image_a')] +[String] +$ImageA, +# Set the image_b of OBSZoomBlurTransitionShader +[Alias('image_b')] +[ComponentModel.DefaultBindingProperty('image_b')] +[String] +$ImageB, +# Set the transition_time of OBSZoomBlurTransitionShader +[Alias('transition_time')] +[ComponentModel.DefaultBindingProperty('transition_time')] +[Single] +$TransitionTime, +# Set the convert_linear of OBSZoomBlurTransitionShader +[Alias('convert_linear')] +[ComponentModel.DefaultBindingProperty('convert_linear')] +[Management.Automation.SwitchParameter] +$ConvertLinear, +# Set the strength of OBSZoomBlurTransitionShader +[ComponentModel.DefaultBindingProperty('strength')] +[Single] +$Strength, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterIndex')] -[ValidateRange(0,[int]::MaxValue)] -[double] -$FilterIndex, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'zoom_blur_transition' +$ShaderNoun = 'OBSZoomBlurTransitionShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +//based on https://www.shadertoy.com/view/Ml3XR2 +uniform texture2d image_a; +uniform texture2d image_b; +uniform float transition_time = 0.5; +uniform bool convert_linear = true; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } +//modified zoom blur from http://transitions.glsl.io/transition/b86b90161503a0023231 +uniform float strength< + string label = "Strength (0.3)"; + string widget_type = "slider"; + float minimum = 0.00; + float maximum = 1.50; + float step = 0.01; +> = 0.3; +#define PI 3.141592653589793 +float Linear_ease(in float begin, in float change, in float duration, in float time) { + return change * time / duration + begin; } +float Exponential_easeInOut(in float begin, in float change, in float duration, in float time) { + if (time == 0.0) + return begin; + else if (time == duration) + return begin + change; + time = time / (duration / 2.0); + if (time < 1.0) + return change / 2.0 * pow(2.0, 10.0 * (time - 1.0)) + begin; + return change / 2.0 * (-pow(2.0, -10.0 * (time - 1.0)) + 2.0) + begin; +} -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterName { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterName')] -[Alias('obs.powershell.websocket.SetSourceFilterName')] -param( - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] -$SourceName, +float Sinusoidal_easeInOut(in float begin, in float change, in float duration, in float time) { + return -change / 2.0 * (cos(PI * time / duration) - 1.0) + begin; +} -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, +float random(in float3 scale, in float seed) { + return frac(sin(dot(float3(seed, seed, seed), scale)) * 43758.5453 + seed); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] -$FilterName, +float3 crossFade(in float2 uv, in float dissolve) { + return lerp(image_a.Sample(textureSampler, uv).rgb, image_b.Sample(textureSampler, uv).rgb, dissolve); +} -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('newFilterName')] -[string] -$NewFilterName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +float4 mainImage(VertData v_in) : TARGET { + float2 texCoord = v_in.uv; + float progress = transition_time; + // Linear interpolate center across center half of the image + float2 center = float2(Linear_ease(0.5, 0.0, 1.0, progress),0.5); + float dissolve = Exponential_easeInOut(0.0, 1.0, 1.0, progress); + // Mirrored sinusoidal loop. 0->strength then strength->0 + float strength2 = Sinusoidal_easeInOut(0.0, strength, 0.5, progress); -process { + float3 color = float3(0.0,0.0,0.0); + float total = 0.0; + float2 toCenter = center - texCoord; + /* randomize the lookup values to hide the fixed float of samples */ + float offset = random(float3(12.9898, 78.233, 151.7182), 0.0)*0.5; - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + for (float t = 0.0; t <= 20.0; t++) { + float percent = (t + offset) / 20.0; + float weight = 1.0 * (percent - percent * percent); + color += crossFade(texCoord + toCenter * percent * strength2, dissolve) * weight; + total += weight; + } + float4 rgba = float4(color / total, 1.0); + if (convert_linear) + rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb); + return rgba; +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -63151,125 +62106,177 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSSourceFilterSettings { - +function Get-OBSZoomShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetSourceFilterSettings')] -[Alias('obs.powershell.websocket.SetSourceFilterSettings')] +[Alias('Set-OBSZoomShader','Add-OBSZoomShader')] param( - +# Set the center_x_percent of OBSZoomShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the power of OBSZoomShader +[ComponentModel.DefaultBindingProperty('power')] +[Single] +$Power, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceName')] -[string] +[Alias('SceneItemName')] +[String] $SourceName, - +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('sourceUuid')] -[string] -$SourceUuid, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterName')] -[string] +[String] $FilterName, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('filterSettings')] -[PSObject] -$FilterSettings, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('overlay')] -[switch] -$Overlay, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] $PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'zoom' +$ShaderNoun = 'OBSZoomShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float power< + string label = "power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1.0; +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -63278,110 +62285,197 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStreamServiceSettings { - +function Get-OBSZoomXYShader { -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStreamServiceSettings')] -[Alias('obs.powershell.websocket.SetStreamServiceSettings')] +[Alias('Set-OBSZoomXYShader','Add-OBSZoomXYShader')] param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceType')] -[string] -$StreamServiceType, - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('streamServiceSettings')] -[PSObject] -$StreamServiceSettings, -# If set, will return the information that would otherwise be sent to OBS. +# Set the center_x_percent of OBSZoomXYShader +[Alias('center_x_percent')] +[ComponentModel.DefaultBindingProperty('center_x_percent')] +[Int32] +$CenterXPercent, +# Set the center_y_percent of OBSZoomXYShader +[Alias('center_y_percent')] +[ComponentModel.DefaultBindingProperty('center_y_percent')] +[Int32] +$CenterYPercent, +# Set the x_power of OBSZoomXYShader +[Alias('x_power')] +[ComponentModel.DefaultBindingProperty('x_power')] +[Single] +$XPower, +# Set the y_power of OBSZoomXYShader +[Alias('y_power')] +[ComponentModel.DefaultBindingProperty('y_power')] +[Single] +$YPower, +# The name of the source. This must be provided when adding an item for the first time [Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors +[Alias('SceneItemName')] +[String] +$SourceName, +# The name of the filter. If this is not provided, this will default to the shader name. [Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse +[String] +$FilterName, +# The inline value of the shader. This will normally be provided as a default parameter, based off of the name. +[Alias('ShaderContent')] +[String] +$ShaderText, +# If set, will force the recreation of a shader that already exists +[Management.Automation.SwitchParameter] +$Force, +# If set, will pass thru the commands that would be sent to OBS (these can be sent at any time with Send-OBS) +[Management.Automation.SwitchParameter] +$PassThru, +# If set, will not wait for a response from OBS (this will be faster, but will not return anything) +[Management.Automation.SwitchParameter] +$NoResponse, +# If set, use the shader elapsed time, instead of the OBS system elapsed time +[ComponentModel.DefaultBindingProperty('use_shader_elapsed_time')] +[Management.Automation.SwitchParameter] +$UseShaderTime ) process { +$shaderName = 'Zoom_XY' +$ShaderNoun = 'OBSZoomXYShader' +if (-not $psBoundParameters['ShaderText']) { + $psBoundParameters['ShaderText'] = $ShaderText = ' +// Zoom XY Shader +// A simple twist on the Zoom Shader in https://github.com/exeldro/obs-shaderfilter/ - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand +// The allow for an independent Horizontal and Vertical Zoom. + +uniform int center_x_percent< + string label = "center x percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform int center_y_percent< + string label = "center y percent"; + string widget_type = "slider"; + int minimum = 0; + int maximum = 100; + int step = 1; +> = 50; +uniform float x_power< + string label = "x power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +uniform float y_power< + string label = "y power"; + string widget_type = "slider"; + float minimum = 0; + float maximum = 20.0; + float step = 0.001; +> = 1; + +float4 mainImage(VertData v_in) : TARGET +{ + float2 center_pos = float2(center_x_percent * .01, center_y_percent * .01); + float2 uv = v_in.uv; + uv.x = (v_in.uv.x - center_pos.x) * x_power + center_pos.x; + uv.y = (v_in.uv.y - center_pos.y) * y_power + center_pos.y; + return image.Sample(textureSampler, uv); +} +' +} +$MyVerb, $myNoun = $MyInvocation.InvocationName -split '-',2 +if (-not $myNoun) { + $myNoun = $myVerb + $myVerb = 'Get' +} +switch -regex ($myVerb) { + Get { + $FilterNamePattern = "(?>$( + if ($FilterName) { + [Regex]::Escape($FilterName) + } + else { + [Regex]::Escape($ShaderNoun -replace '^OBS' -replace 'Shader$'),[Regex]::Escape($shaderName) -join '|' + } + ))" + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } else { + $obs.Inputs | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern + } + } + 'Remove' { + if ($SourceName) { + Get-OBSInput | + Where-Object InputName -eq $SourceName | + Get-OBSSourceFilterList | + Where-Object FilterName -Match $FilterNamePattern | + Remove-OBSSourceFilter + } + } + '(?>Add|Set)' { + $ShaderSettings = [Ordered]@{} + :nextParameter foreach ($parameterMetadata in $MyInvocation.MyCommand.Parameters[@($psBoundParameters.Keys)]) { + foreach ($parameterAttribute in $parameterMetadata.Attributes) { + if ($parameterAttribute -isnot [ComponentModel.DefaultBindingPropertyAttribute]) { continue } + $ShaderSettings[$parameterAttribute.Name] = $PSBoundParameters[$parameterMetadata.Name] + if ($ShaderSettings[$parameterAttribute.Name] -is [switch]) { + $ShaderSettings[$parameterAttribute.Name] = $ShaderSettings[$parameterAttribute.Name] -as [bool] + } + continue nextParameter + } + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $PSBoundParameters['FilterName']) { + $filterName = $PSBoundParameters['FilterName'] = $shaderName } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + $ShaderFilterSplat = [Ordered]@{ + ShaderSetting = $ShaderSettings + FilterName = $FilterName + SourceName = $SourceName + } + + foreach ($CarryOnParameter in "PassThru", "NoResponse","Force") { + if ($PSBoundParameters.ContainsKey($CarryOnParameter)) { + $ShaderFilterSplat[$CarryOnParameter] = $PSBoundParameters[$CarryOnParameter] } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + if (-not $script:CachedShaderFilesFromCommand) { + $script:CachedShaderFilesFromCommand = @{} } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + + if ($Home -and -not $script:CachedShaderFilesFromCommand[$shaderName]) { + $MyObsPowerShellPath = Join-Path $home ".obs-powershell" + $ThisShaderPath = Join-Path $MyObsPowerShellPath "$shaderName.shader" + $shaderText | Set-Content -LiteralPath $ThisShaderPath + $script:CachedShaderFilesFromCommand[$shaderName] = Get-Item -LiteralPath $ThisShaderPath } + if ($script:CachedShaderFilesFromCommand[$shaderName]) { + $ShaderFilterSplat.ShaderFile = $script:CachedShaderFilesFromCommand[$shaderName].FullName + } else { + $ShaderFilterSplat.ShaderText = $shaderText + } - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($myVerb -eq 'Add') { + Add-OBSShaderFilter @ShaderFilterSplat } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + Set-OBSShaderFilter @ShaderFilterSplat } + } +} } @@ -63390,2510 +62484,3415 @@ process { #.ExternalHelp obs-powershell-Help.xml -function Set-OBSStudioModeEnabled { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetStudioModeEnabled')] -[Alias('obs.powershell.websocket.SetStudioModeEnabled')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('studioModeEnabled')] -[switch] -$StudioModeEnabled, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +function Set-OBSAudioOutputSource { + + + #> + [Alias('Add-OBSAudioOutputSource','Get-OBSAudioOutputSource')] + param( + # The name of the audio device. + # This name or device ID of the audio device that should be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('ItemValue','ItemName','DeviceID')] + [string] + $AudioDevice, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, -process { + # The name of the input. + # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + begin { + # Audio Output sources have an inputKind of 'wasapi_output_capture'. + $inputKind = "wasapi_output_capture" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + + if (-not $myParameters["AudioDevice"]) { + $myParameters["AudioDevice"] = "default" } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSTBarPosition { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetTBarPosition')] -[Alias('obs.powershell.websocket.SetTBarPosition')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('position')] -[ValidateRange(0,1)] -[double] -$Position, - -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('release')] -[switch] -$Release, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + + if ($myParameters["AudioDevice"]) { + $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"] + } + else { + $Name = $myParameters["Name"] = "AudioOutput" + } + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] } } + } + + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = $inputKind + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } + + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id + foreach ($deviceInfo in $possibleDevices) { + if ( + ($deviceInfo.itemName -eq $AudioDevice) -or + ($deviceInfo.ItemValue -eq $AudioDevice) -or + ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or + ($deviceInfo.ItemValue -like "*$AudioDevice*") -or + ($deviceInfo.ItemName -like "*$AudioDevice*") + ) { + $myParameterData["device_id"] = $deviceInfo.itemValue + break + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return } + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Scene"] + } + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + } + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSBrowserSource { + + + [Alias('Add-OBSBrowserSource','Get-OBSBrowserSource')] + param( + # The uri or file path to display. + # If the uri points to a local file, this will be preferred + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url', 'Href','Path','FilePath','FullName')] + [uri] + $Uri, + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, - -#.ExternalHelp obs-powershell-Help.xml -function Set-OBSVideoSettings { + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'SetVideoSettings')] -[Alias('obs.powershell.websocket.SetVideoSettings')] -param( + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsNumerator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsNumerator, + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('fpsDenominator')] -[ValidateRange(1,[int]::MaxValue)] -[double] -$FpsDenominator, + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseWidth')] -[ValidateRange(1,4096)] -[double] -$BaseWidth, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('baseHeight')] -[ValidateRange(1,4096)] -[double] -$BaseHeight, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputWidth')] -[ValidateRange(1,4096)] -[double] -$OutputWidth, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputHeight')] -[ValidateRange(1,4096)] -[double] -$OutputHeight, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -process { + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + $inputKind = "browser_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } + if ($uri.Scheme -eq 'File') { + if (Test-Path $uri.AbsolutePath) { + $myParameterData["local_file"] = "$uri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + else + { + if (Test-Path $uri) { + $rp = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($uri) + $myParameterData["local_file"] = "$rp" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true + } else { + $myParameterData["url"] = "$uri" + } } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($uri.Segments) { + $uri.Segments[-1] + } elseif ($uri -match '[\\/]') { + @($uri -split '[\\/]')[-1] + } else { + $uri + } + } -} + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 -} + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Start-OBSOutput { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartOutput')] -[Alias('obs.powershell.websocket.StartOutput')] -param( - -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) +function Set-OBSColorSource { + + + [Alias('Add-OBSColorSource','Get-OBSColorSource')] + param( + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, -process { + [ValidatePattern('\#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})')] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + } + begin { + $inputKind = "color_source_v3" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartRecord')] -[Alias('obs.powershell.websocket.StartRecord')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - - - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $hexChar = [Regex]::new('[0-9a-f]') + $hexColors = @($hexChar.Matches($Color)) - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + switch ($hexColors.Length) { + 8 { + #full rgba + $alpha = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $red = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[6..7] -join '', 'HexNumber') + } + 6 { + #rgb only, assume ff for alpha + $alpha = 0xff + $red = [byte]::Parse($hexColors[0..1] -join '', 'HexNumber') + $green = [byte]::Parse($hexColors[2..3] -join '', 'HexNumber') + $blue = [byte]::Parse($hexColors[4..5] -join '', 'HexNumber') + } + 4 { + #short rgba + $alpha = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $red = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[3],$hexColors[3] -join ''), 'HexNumber') + } + 3 { + #short rgb, assume f for alpha + $alpha = 0xff + $red = [byte]::Parse(($hexColors[0],$hexColors[0] -join ''), 'HexNumber') + $green = [byte]::Parse(($hexColors[1],$hexColors[1] -join ''), 'HexNumber') + $blue = [byte]::Parse(($hexColors[2],$hexColors[2] -join ''), 'HexNumber') + } + 0 { + # No color provided, default to transparent black + $alpha = 0 + $red = 0 + $green = 0 + $blue = 0 + } } + + $hexColor = ("{0:x2}{1:x2}{2:x2}{3:x2}" -f $alpha, $blue, $green, $red) - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + $realColor = [uint32]::Parse($hexColor,'HexNumber') + + + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "#$hexColor" } + + $myParameterData = [Ordered]@{color=$realColor} - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "color_source_v3" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } -} + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } -} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } +} #.ExternalHelp obs-powershell-Help.xml -function Start-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartReplayBuffer')] -[Alias('obs.powershell.websocket.StartReplayBuffer')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - +function Set-OBSDisplaySource { + + + [Alias('Add-OBSMonitorSource','Set-OBSMonitorSource','Add-OBSDisplaySource')] + param( + # The monitor number. + # This the number of the monitor you would like to capture. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("monitor")] + [Alias('MonitorNumber','Display','DisplayNumber')] + [int] + $Monitor = 1, -process { + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("capture_cursor")] + [switch] + $CaptureCursor, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } } - - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -} - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSStream { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartStream')] -[Alias('obs.powershell.websocket.StartStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool] + } + } + } -process { + # Users like 1 indexed, computers like zero-indexed. + $myParameterData["monitor"] = $Monitor - 1 + if (-not $myParameters["Name"]) { + $myParameters["Name"] = "Display $($Monitor)" + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "monitor_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSMarkdownSource { + + + [Alias('Add-OBSMarkdownSource','Get-OBSMarkdownSource')] + param( + # The markdown text, or the path to a markdown file + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Markdown, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, -} + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + # The css style used to render the markdown. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", -} + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, - -#.ExternalHelp obs-powershell-Help.xml -function Start-OBSVirtualCam { + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StartVirtualCam')] -[Alias('obs.powershell.websocket.StartVirtualCam')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters -process { + } + begin { + $inputKind = "markdown_source" + + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters + $IsGet = $MyInvocation.InvocationName -like "Get-*" + $NoVerb = $MyInvocation.InvocationName -match '^[^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if ( + $IsGet -or + ($NoVerb -and -not $NonNameParameters) + ) { + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } + $markdownAsUri = $null + if ($Markdown -like '*.md') { + $markdownAsUri = $markdown -as [uri] + if ($markdownAsUri.Scheme -eq 'File') { + $myParameterData["markdown_path"] = "$markdownAsUri" -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["markdown_source"] = 1 + } + else { + } + } else { + $myParameterData["text"] = $Markdown + $myParameterData["markdown_source"] = 0 } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($markdownAsUri.Segments) { + $markdownAsUri.Segments[-1] + } elseif ($markdownAsUri -match '[\\/]') { + @($markdownAsUri -split '[\\/]')[-1] + } elseif ($markdownAsUri) { + $markdownAsUri + } else { + "Markdown" + } + } + + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = "markdown_source" + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } + } + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + } + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSMediaSource { + + + [Alias('Add-OBSFFMpegSource','Add-OBSMediaSource','Set-OBSFFMpegSource','Get-OBSFFMpegSource','Get-OBSMediaSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file')] + [string] + $FilePath, + # If set, the source will close when it is inactive. + # By default, this will be set to true. + # To explicitly set it to false, use -CloseWhenInactive:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("close_when_inactive")] + [switch] + $CloseWhenInactive, -} + # If set, the source will automatically restart. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("looping")] + [Alias('Looping')] + [switch] + $Loop, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSOutput { + # If set, will use hardware decoding, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("hw_decode")] + [Alias('HardwareDecoding','hw_decode')] + [switch] + $UseHardwareDecoding, + # If set, will clear the output on the end of the media. + # If this is set to false, the media will freeze on the last frame. + # This is set to true by default. + # To explicitly set to false, use -ClearMediaEnd:$false + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("clear_on_media_end")] + [Alias('ClearOnEnd','NoFreezeFrameOnEnd')] + [switch] + $ClearOnMediaEnd, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopOutput')] -[Alias('obs.powershell.websocket.StopOutput')] -param( + # Any FFMpeg demuxer options. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ffmpeg_options")] + [Alias('FFMpegOptions', 'FFMpeg_Options')] + [string] + $FFMpegOption, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -process { + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "ffmpeg_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ((Test-Path $FilePath)) { + $FilePathItem = Get-Item -Path $FilePath + $myParameterData['local_file'] = $FilePathItem.FullName -replace '/', '\' } -} + + if ($myParameters['InputSettings']) { + $keys = + @(if ($myParameters['InputSettings'] -is [Collections.IDictionary]) { + $myParameters['InputSettings'].Keys + } else { + foreach ($prop in $myParameters['InputSettings'].PSObject.Properties) { + $prop.Name + } + }) -} + foreach ($key in $keys) { + $myParameterData[$key] = $myParameters['InputSettings'].$key + } + $myParameterData.remove('inputSettings') + } -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSRecord { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopRecord')] -[Alias('obs.powershell.websocket.StopRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - + if (-not $Name) { + + $Name = $myParameters["Name"] = + if ($FilePathItem.Name) { + $FilePathItem.Name + } else { + "Media" + } + + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # Otherwise, if we had a result + if ($outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } - + + } } + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSSoundCloudSource { + + + [Alias('Add-OBSSoundCloudSource','Get-OBSSoundCloudSource')] + param( + # The uri to display. This must point to a SoundCloud URL. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Url','SoundCloudUri','SoundCloudUrl')] + [uri] + $Uri, + # If set, will not autoplay. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoAutoPlay, -} + # If set, will not display album artwork. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoArtwork, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSReplayBuffer { + # If set, will not display play count. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoPlayCount, + # If set, will not display uploader info. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $NoUploaderInfo, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopReplayBuffer')] -[Alias('obs.powershell.websocket.StopReplayBuffer')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If provided, will start playing at a given track number. + [Parameter(ValueFromPipelineByPropertyName)] + [int] + $TrackNumber, + # If set, will show a share link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowShare, -process { + # If set, will show a download link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowDownload, + # If set, will show a buy link. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $ShowBuy, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The color used for the SoundCloud audio bars and buttons. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Color, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + # The css style used to render the browser page. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("css")] + [string] + $CSS = "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }", - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # If set, the browser source will shutdown when it is hidden + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shutdown")] + [switch] + $ShutdownWhenHidden, -} + # If set, the browser source will restart when it is activated. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("restart_when_active")] + [switch] + $RestartWhenActived, + # If set, audio from the browser source will be rerouted into OBS. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("reroute_audio")] + [switch] + $RerouteAudio, -} + # If provided, the browser source will render at a custom frame rate. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fps")] + [Alias('FPS')] + [int] + $FramesPerSecond, - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSStream { + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, + # The name of the input. + # If no name is provided, then "SoundCloud" will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopStream')] -[Alias('obs.powershell.websocket.StopStream')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -process { + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters + } + begin { + # Browser Sources are built into OBS. Their input kind is browser_source. + # Sound Cloud Sources are really Browser Sources. + $inputKind = "browser_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if (-not $uri.DnsSafeHost -or $uri.DnsSafeHost -notmatch 'SoundCloud\.com$') { + Write-Error "URI must be from SoundCloud.com" + return } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + if ($uri.Query) { + $uri = "https://$($uri.DnsSafeHost)" + $($uri.Segments -join '') } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } | + Where-Object { + $_.Settings['LocalFile'] -like '*.SoundCloud.*' } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy + # (either way, if we were called Get- or with no verb, we're done now). + return } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight } -} + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } -} + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } - -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSVirtualCam { + if ($fps -and $fps -ne 30) { + $myParameterData["custom_fps"] = $true + } + $MyObsPowerShellPath = if ($home) { + Join-Path $home ".obs-powershell" + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'StopVirtualCam')] -[Alias('obs.powershell.websocket.StopVirtualCam')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + $ThisSoundCloudSourceFileName = + if ($name) { + "${name}.SoundCloudSource.html" + } else { + "SoundCloudSource.html" + } + $ThisSoundCloudSourceFilePath = Join-Path $MyObsPowerShellPath $ThisSoundCloudSourceFileName + -process { + $soundCloudSrc = @( + "https://w.soundcloud.com/player/?url=" + $Uri + "&" + @( + if ($PSBoundParameters["TrackNumber"]) {"start_track=$trackNumber"} + if ($color) { "color=$color" -replace "\#",'%23'} + if ($NoAutoPlay) { "auto_play=false" } else { "auto_play=true"} + if ($NoArtwork) { "show_artwork=false" } else {"show_artwork=true" } + if ($NoUploaderInfo) { "show_user=false" } else {"show_user=true"} + if ($NoPlayCount) { "show_playcount=false" } else {"show_playcount=true" } + if ($ShowDownload) { "download=true"} else { "download=false" } + if ($ShowBuy) { "buying=true"} else { "buying=false" } + if ($ShowShare) { "sharing=true"} else { "sharing=false" } + ) -join '&' + ) -join '' + + $soundCloudWidget = @( + "" + "" + "" + "" + "" + ) -join ' ' + $newHtmlFile = New-Item -Value $soundCloudWidget -ItemType File -Path $ThisSoundCloudSourceFilePath -Force + + $myParameterData["local_file"] = ([uri]$newHtmlFile.FullName) -replace '[\\/]', '/' -replace '^file:///' + $myParameterData["is_local_file"] = $true - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + if (-not $Name) { + $Name = $myParameters['Name'] = 'SoundCloud' + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + } + + } } - - -} - #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSInputMute { +function Set-OBSSwitchSource { + + + [Alias('Add-OBSSwitchSource','Get-OBSSwitchSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('Sources')] + [string[]] + $SourceList, + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) + + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string'." + } + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleInputMute')] -[Alias('obs.powershell.websocket.ToggleInputMute')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + # If set, the list of sources will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputName')] -[string] -$InputName, + # If set, will switch between sources. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch")] + [switch] + $TimeSwitch, -[Parameter(ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('inputUuid')] -[string] -$InputUuid, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The interval between sources + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_between")] + [timespan] + $Interval, + # The duration between sources that are switching at a time. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("time_switch_duration")] + [timespan] + $Duration, -process { + # The item that will be switched in a TimeSwitch, after -Duration and -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $TimeSwitchTo = "Next", + # If set, will switch on the underlying source's media state events. + # Sources will be displayed for a -Duration. + # No source wil be displayed for an -Interval. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("media_state_switch")] + [switch] + $MediaStateSwitch, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The change in media state that should trigger a switch + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("Playing","Opening","Buffering","Paused","Stopped","Ended", "Error","Playing","NotOpening","NotBuffering","NotPaused","NotStopped","NotEnded", "NotError")] + $MediaStateChange, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # When the source switcher is trigger by media end, this determines the next source that will be switched to. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet("None","Next","Previous","First","Last","Random")] + [string] + $MediaSwitchTo = "Next", - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } + # The name of the transition between sources. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + } + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $TransitionName, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } + # The properties sent to the transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $TransitionProperty, + + # The name of the transition used to show a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $ShowTransition, -} - + # The properties sent to the show transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $ShowTransitionProperty, -} + # The transition used to hide a source. + [ArgumentCompleter({ + param ( $commandName, + $parameterName, + $wordToComplete, + $commandAst, + $fakeBoundParameters ) + if (-not $script:OBSTransitionKinds) { + $script:OBSTransitionKinds = @(Get-OBSTransitionKind) -replace '_transition$' + } + + if ($wordToComplete) { + $toComplete = $wordToComplete -replace "^'" -replace "'$" + return @($script:OBSTransitionKinds -like "$toComplete*" -replace '^', "'" -replace '$',"'") + } else { + return @($script:OBSTransitionKinds -replace '^', "'" -replace '$',"'") + } + })] + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $HideTransition, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSOutput { + # The properties sent to the hide transition. + # Notice: this current requires confirmation in the UI. + [Parameter(ValueFromPipelineByPropertyName)] + [PSObject] + $HideTransitionProperty, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleOutput')] -[Alias('obs.powershell.websocket.ToggleOutput')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName','SourceName')] + [string] + $Name, -[Parameter(Mandatory,ValueFromPipelineByPropertyName)] -[ComponentModel.DefaultBindingProperty('outputName')] -[string] -$OutputName, -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' -process { + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() + } + $_ + + } + $InputKind = "source_switcher" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind + } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } } - } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + if ($myParameters[$parameter.Name] -is [timespan]) { + $myParameterData[$bindToPropertyName] = [int]$myParameters[$parameter.Name].TotalMilliseconds } } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + + $selectedIndex = -1 + $sourcesObject = @( + $currentIndex = -1 + foreach ($sourceName in $SourceList) { + $currentIndex++ + $selected = ($null -ne $Select) -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [string] -and + ($sourceName -like $select -or ($sourceName | Split-Path -Leaf -ErrorAction Ignore) -like $Select) + ) + ) + if ($selected) { + $selectedIndex = $currentIndex + } + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$sourceName} + } + ) - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if ($sourcesObject) { + $myParameterData['sources'] = $sourcesObject + if ($selectedIndex -gt 0) { + $myParameterData["current_index"] = $selectedIndex + } + } + elseif ($Select -is [int]) { + $myParameterData['current_index'] = $Select } + -} + + if ($TransitionName) { + if ($TransitionName -notlike '*_transition') { + $TransitionName = "${TransitionName}_transition" + } + $myParameterData["transition"] = $TransitionName + } + + if ($TransitionProperty) { + $myParameterData["transition_properties"] = $TransitionProperty + } + if ($ShowTransition) { + if ($ShowTransition -notlike '*_transition') { + $ShowTransition = "${ShowTransition}_transition" + } + $myParameterData["show_transition"] = $ShowTransition + } -} + if ($ShowTransitionProperty) { + $myParameterData["show_transition_properties"] = $ShowTransitionProperty + } - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecord { + if ($HideTransition) { + if ($HideTransition -notlike '*_transition') { + $HideTransition = "${HideTransition}_transition" + } + $myParameterData["hide_transition"] = $ShowTransition + } + if ($HideTransitionProperty) { + $myParameterData["hide_transition_properties"] = $HideTransitionProperty + } -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecord')] -[Alias('obs.powershell.websocket.ToggleRecord')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + if ($TimeSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["TimeSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["time_switch_to"] = $vvi + break + } + } + } + if ($MediaSwitchTo) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaSwitchTo"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_state_switch_to"] = $vvi + break + } + } + } -process { + if ($MediaStateChange) { + $validValues = $MyInvocation.MyCommand.Parameters["MediaStateChange"].Attributes.ValidValues + for ($vvi = 0; $vvi -lt $validValues.Length;$vvi++) { + if ($TimeSwitchTo -eq $validValues[$vvi]) { + $myParameterData["media_switch_state"] = $vvi + break + } + } + } + + if (-not $Name) { + $Name = $myParameters["Name"] = "Source Switcher" + } + - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + if ($sceneItem) { + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null + } } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen + } + + } +} + +#.ExternalHelp obs-powershell-Help.xml +function Set-OBSVLCSource { + + + [Alias('Add-OBSVLCSource','Set-OBSPlaylistSource','Add-OBSPlaylistSource','Get-OBSVLCSource','Get-OBSPlaylistSource')] + param( + # The path to the media file. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('FullName','LocalFile','local_file','Playlist')] + [string[]] + $FilePath, + + # What to select in the playlist. + # If a number is provided, this will select an index. + # If a string is provided, this will select the whole name or last part of a name, accepting wildcards. + # If an `[IO.FileInfo]` is provided, this will be the exact file. + [Parameter(ValueFromPipelineByPropertyName)] + [ValidateScript({ + $validTypeList = [System.Int32],[System.String],[System.IO.FileInfo] + + $thisType = $_.GetType() + $IsTypeOk = + $(@( foreach ($validType in $validTypeList) { + if ($_ -as $validType) { + $true;break + } + })) - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + if (-not $isTypeOk) { + throw "Unexpected type '$(@($thisType)[0])'. Must be 'int','string','System.IO.FileInfo'." + } + return $true + })] + + [Alias('SelectIndex','SelectName')] + $Select, -} + # If set, will shuffle the playlist + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("shuffle")] + [switch] + $Shuffle, + # If set, the playlist will loop. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("loop")] + [Alias('Looping')] + [switch] + $Loop, -} + # If set, will show subtitles, if available. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle_enable")] + [Alias('ShowSubtitles','Subtitles')] + [switch] + $Subtitle, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSRecordPause { + # The selected audio track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("track")] + [int] + $AudioTrack, + # The selected subtitle track number. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("subtitle")] + [int] + $SubtitleTrack, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleRecordPause')] -[Alias('obs.powershell.websocket.ToggleRecordPause')] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Scene, + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Name, -process { + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force, + # If set, will fit the input to the screen. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $FitToScreen + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } - - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} } - } + if (-not $shouldInclude) { continue nextInputParameter } } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] + } + begin { + filter OutputAndFitToScreen { + + if ($FitToScreen -and $_.FitToScreen) { + $_.FitToScreen() } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" + $_ + + } + $InputKind = "vlc_source" + + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } } - continue nextParam - } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters + } else { + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { -} - - -} + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break + } + } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] + } + } + } + + $allPaths = @(foreach ($path in $FilePath) { + foreach ($_ in $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($path)) { + $_.Path + } + }) + $playlistObject = @( + $currentIndex = 0 + foreach ($path in $allPaths) { + $currentIndex++ + $selected = $Select -and ( + ($select -is [int] -and $currentIndex -eq $select) -or + ($select -is [IO.FileInfo] -and $path -eq $select.FullName) -or + ($select -is [string] -and + ($path -like $select -or ($path | Split-Path -Leaf) -like $Select) + ) + ) + [PSCustomObject][Ordered]@{hidden=$false;selected=$selected;value=$path} + } + ) + $myParameterData['playlist'] = $playlistObject -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSReplayBuffer { - - -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleReplayBuffer')] -[Alias('obs.powershell.websocket.ToggleReplayBuffer')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) - - -process { - + if (-not $Name) { + $Name = $myParameters["Name"] = $FilePathItem.Name + } - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $InputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} + if ($myParameters.Contains('SceneItemEnabled')) { + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return } - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } } + + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } - - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene and optionally fit it to the screen. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name | + OutputAndFitToScreen } - + + } } - - -} - #.ExternalHelp obs-powershell-Help.xml -function Switch-OBSStream { +function Set-OBSWaveformSource { + + + [Alias('Add-OBSWaveformSource','Get-OBSWaveformSource')] + param( + # The width of the browser source. + # If none is provided, this will be the output width of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("width")] + [int] + $Width, + + # The width of the browser source. + # If none is provided, this will be the output height of the video settings. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("height")] + [int] + $Height, + # The audio source for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("audio_source")] + [string] + $AudioSource, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleStream')] -[Alias('obs.powershell.websocket.ToggleStream')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The display mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("display_mode")] + [ValidateSet("curve","bars","stepped_bars","level_meter","stepped_level_meter")] + [string] + $DisplayMode, + # The render mode for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("line","solid","gradient")] + [string] + $RenderMode, -process { + # The windowing mode for the waveform. + # This is the mathematical function used to determine the current "window" of audio data. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("render_mode")] + [ValidateSet("hann","hamming","blackman","blackman_harris","none")] + [string] + $WindowMode, + + # The color used for the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_base")] + [PSObject] + $Color, + # The crest color used for the waveform. + # This will be ignored if the render mode is not "gradient". + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("color_crest")] + [PSObject] + $CrestColor, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The channel mode for the waveform. + # This can be either mono or stereo. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_mode")] + [ValidateSet("mono","stereo")] + [string] + $ChannelMode, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + # The number of pixels between each channel in stereo mode + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("channel_spacing")] + [int] + $ChannelSpacing, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + # If set, will use a radial layout for the waveform + # Radial layouts will ignore the desired height of the source and instead create a square. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("radial_layout")] + [switch] + $RadialLayout, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + # If set, will invert the direction for a radial waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("invert_direction")] + [switch] + $InvertRadialDirection, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + # If set, will normalize the volume displayed in the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("normalize_volume")] + [switch] + $NoramlizeVolume, -} + # If set, will automatically declare an FFTSize + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("auto_fft_size")] + [switch] + $AutoFftSize, + # If set, will attempt to make audio peaks render faster. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("fast_peaks")] + [switch] + $FastPeak, -} + # The width of the waveform bar. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_width")] + [int] + $BarWidth, - -#.ExternalHelp obs-powershell-Help.xml -function Switch-OBSVirtualCam { + # The gap between waveform bars. + # This is only valid when -DisplayMode is 'bars' or 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("bar_gap")] + [int] + $BarGap, + # The width of waveform bar step. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_width")] + [int] + $StepWidth, -[Reflection.AssemblyMetadata('OBS.WebSocket.RequestType', 'ToggleVirtualCam')] -[Alias('obs.powershell.websocket.ToggleVirtualCam')] -[Reflection.AssemblyMetadata('OBS.WebSocket.ExpectingResponse', $true)] -param( -# If set, will return the information that would otherwise be sent to OBS. -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('OutputRequest','OutputInput')] -[switch] -$PassThru, -# If set, will not attempt to receive a response from OBS. -# This can increase performance, and also silently ignore critical errors -[Parameter(ValueFromPipelineByPropertyName)] -[Alias('NoReceive','IgnoreResponse','IgnoreReceive','DoNotReceiveResponse')] -[switch] -$NoResponse -) + # The gap between waveform bar steps. + # This is only valid when -DisplayMode is 'stepped_bars' + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("step_gap")] + [int] + $StepGap, + # The low-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_low")] + [int] + $LowCutoff, -process { + # The high-frequency cutoff of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("cutoff_high")] + [int] + $HighCutoff, + # The floor of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("floor")] + [int] + $Floor, - # Create a copy of the parameters (that are part of the payload) - $paramCopy = [Ordered]@{} - # get a reference to this command - $myCmd = $MyInvocation.MyCommand + # The ceiling of the waveform. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("ceiling")] + [int] + $Ceiling, - # Keep track of how many requests we have done of a given type - # (this makes creating RequestIDs easy) - if (-not $script:ObsRequestsCounts) { - $script:ObsRequestsCounts = @{} - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("slope")] + [double] + $Slope, - # Set my requestType to blank - $myRequestType = '' - # and indicate we are not expecting a response - $responseExpected = $false - # Then walk over this commands' attributes, - foreach ($attr in $myCmd.ScriptBlock.Attributes) { - if ($attr -is [Reflection.AssemblyMetadataAttribute]) { - if ($attr.Key -eq 'OBS.WebSocket.RequestType') { - $myRequestType = $attr.Value # set the requestType, - } - elseif ($attr.Key -eq 'OBS.WebSocket.ExpectingResponse') { - # and determine if we are expecting a response. - $responseExpected = - if ($attr.Value -eq 'false') { - $false - } else { $true } - } - } - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_q")] + [Alias('RollOffOctaves')] + [double] + $RollOffOctave, - # Walk over each parameter - :nextParam foreach ($keyValue in $PSBoundParameters.GetEnumerator()) { - # and walk over each of it's attributes to see if it part of the payload - foreach ($attr in $myCmd.Parameters[$keyValue.Key].Attributes) { - # If the parameter is bound to part of the payload - if ($attr -is [ComponentModel.DefaultBindingPropertyAttribute]) { - # copy it into our payload dicitionary. - $paramCopy[$attr.Name] = $keyValue.Value - # (don't forget to turn switches into booleans) - if ($paramCopy[$attr.Name] -is [switch]) { - $paramCopy[$attr.Name] = [bool]$paramCopy[$attr.Name] - } - if ($attr.Name -like '*path') { - $paramCopy[$attr.Name] = - "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($paramCopy[$attr.Name]))" - } - continue nextParam - } - } - } - - # and make a request ID from that. - $myRequestId = "$myRequestType.$([Guid]::newGuid())" - - # Construct the payload object - $requestPayload = [Ordered]@{ - # It must include a request ID - requestId = $myRequestId - # request type - requestType = $myRequestType - # and optional data - requestData = $paramCopy - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("rolloff_rate")] + [double] + $RollOffRate, - if ($PassThru) { - [PSCustomObject]$requestPayload - } else { - [PSCustomObject]$requestPayload | - Send-OBS -NoResponse:$NoResponse - } + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("grad_ratio")] + [double] + $GradientRatio, -} + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("deadzone")] + [double] + $Deadzone, + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("temporal_smoothing")] + [ValidateSet("none","exp_moving_avg")] + [string] + $TemporalSmoothing, -} + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - -#.ExternalHelp obs-powershell-Help.xml -function Get-OBSEffect -{ - - param( - # The name of the effect. + # The name of the input. + # If no name is provided, the last segment of the URI or file path will be the input name. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectName')] + [Alias('InputName')] [string] - $Name - ) + $Name, - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] + [switch] + $Force + ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput } - } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - process { - if (-not $Name) { - $script:OBSFX.Values - } elseif ($script:OBSFX[$name]) { - $script:OBSFX[$name] + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} + } } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) } -} -#.ExternalHelp obs-powershell-Help.xml -function Import-OBSEffect { + $DynamicParameters - - param( - # The source location of the effect. - # This can be a string, file, directory, command, or module. - [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] - [Alias( - 'FromPath', - 'FromModule', - 'FromScript', - 'FromFunction', - 'FullName', - 'Path', - 'Source' - )] - [ValidateScript({ - $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo] - - $thisType = $_.GetType() - $IsTypeOk = - $(@( foreach ($validType in $validTypeList) { - if ($_ -as $validType) { - $true;break - } - })) - - if (-not $isTypeOk) { - throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'." } - return $true - })] + begin { + $inputKind = "phandasm_waveform_source" + filter ToOBSColor { + + if ($_ -is [uint32]) { $_ } + elseif ($_ -is [string]) { + if ($_ -match '^\#[a-f0-9]{3,4}$') { + $_ = $_ -replace '[a-f0-9]','$0$0' + } + + if ($_ -match '^#[a-f0-9]{8}$') { + ( + '0x' + + (($_ -replace '#').ToCharArray()[0,1,-1,-2,-3,-4,-5,-6] -join '') + ) -as [UInt32] + } + elseif ($_ -match '^#[a-f0-9]{6}$') { + + ( + '0xff' + + (($_ -replace '#').ToCharArray()[-1..-6] -join '') + ) -as [UInt32] + } + } + + } - $From - ) + } + process { + $myParameters = [Ordered]@{} + $PSBoundParameters - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} - } + $MyInvocationName = "$($MyInvocation.InvocationName)" + $myVerb, $myNoun = $MyInvocationName -split '-' + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + $IsGet = $myVerb -eq "Get" + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' - $newEffects = @() - $obsEffectsPattern = [Regex]::new(' - (?> - ^OBS.(?>fx|effects?)\p{P} - | - [\p{P}-[-]]OBS\.(?>fx|effects?)$ - | - \p{P}OBS.(?>fx|effects?)\.(?>ps1|json)$ - ) - ','IgnoreCase,IgnorePatternWhitespace') - } + if ( + $IsGet -or + $NoVerb + ) { + $inputsOfKind = + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { + $_.InputName -like $Name + } else { + $_ + } + } + if ($NonNameParameters -and -not $IsGet) { + $paramCopy = [Ordered]@{} + $PSBoundParameters + if ($paramCopy.Name) { $paramCopy.Remove('Name') } + $inputsOfKind | & $myScriptBlock @paramCopy + } else { + $inputsOfKind + } + return + } + + if ((-not $width) -or (-not $height)) { + if (-not $script:CachedOBSVideoSettings) { + $script:CachedOBSVideoSettings = Get-OBSVideoSettings + } + $videoSettings = $script:CachedOBSVideoSettings + $myParameters["Width"] = $width = $videoSettings.outputWidth + $myParameters["Height"] = $height = $videoSettings.outputHeight + } - process { - # Since -From can be many things, but a metric has to be a command, - # the purpose of this function is to essentially resolve many things to a command, - # and then cache that command. + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { - # If -From was a string - if ($From -is [string]) { - # It could be a module, so check those first. - :ResolveFromString do { - foreach ($loadedModule in @(Get-Module)) { - # If we find the module, don't try to resolve -From as a path - if ($loadedModule.Name -eq $from) { - # (just set -From again and let the function continue) - $from = $fromModule = $loadedModule;break ResolveFromString - } - + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } - # If we think from was a path - $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from) - # attempt to resolve it - if ($resolvedPath) { - $from = Get-Item -LiteralPath $resolvedPath + } + + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } - } while ($false) + } } - # If -From is a module - if ($from -is [Management.Automation.PSModuleInfo]) { - # recursively call ourselves with all matching commands - @($from.ExportedCommands.Values) -match $obsEffectsPattern | - Import-OBSEffect - # then, make -From a directory - if ($from.Path) { - $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue - } + + + if (-not $Name) { + $Name = $myParameters['Name'] = + if ($AudioSource) { + "$($AudioSource)-Waveform" + } else { + "Waveform" + } } - # If -From is a directory - if ($from -is [IO.DirectoryInfo]) { - $FromDirectory = $from - # recursively call ourselves with all matching scripts - Get-ChildItem -LiteralPath $from.FullName -Recurse -File | - Where-Object Name -match '\.obs\.(?>fx|effects?).(?>ps1|json)$' | - Import-OBSEffect - return + if ($myParameterData.color_base) { + $myParameterData.color_base = $myParameterData.color_base | ToOBSColor + } + + if ($myParameterData.color_crest) { + $myParameterData.color_crest = $myParameterData.color_crest | ToOBSColor } - # If -From is a file - if ($from -is [IO.FileInfo]) { - # and it matches the naming convention - if ($from.Name -notmatch '\.obs\.(?>fx|effects?).(?>ps1|json)$') { return } - # make -From a command. - $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript,Application') + $addSplat = [Ordered]@{ + sceneName = $myParameters["Scene"] + inputKind = $inputKind + inputSettings = $myParameterData + inputName = $Name + NoResponse = $myParameters["NoResponse"] + } + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] } - # If -From is a command - if ($from -is [Management.Automation.CommandInfo]) { - # decorate the command - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect') + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + $addSplat.Passthru = $MyParameters["PassThru"] + # If we were called with Add- + if ($MyInvocation.InvocationName -like 'Add-*') { + Add-OBSInput @addSplat # passthru Add-OBSInput + } else { + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat } + return + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 - if ($from -is [Management.Automation.ApplicationInfo]) { - $effectName = $from.Name -replace '\.obs\.(?>fx|effects?).(?>ps1|json)$' - $newEffect = [PSCustomObject][Ordered]@{ - PSTypeName = 'OBS.PowerShell.Effect' - Messages = Get-Content -Raw -Path $From.Source | ConvertFrom-Json - EffectName = $effectName - TypeName = $TypeName - } - $script:OBSFX[$effectName] = $newEffect - $newEffects += $newEffect - $newEffect - } else { - if ($from.pstypenames -notcontains 'OBS.PowerShell.Effect.Command') { - $from.pstypenames.insert(0,'OBS.PowerShell.Effect.Command') + # If we got back an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # and that error was saying the source already exists, + if ($outputAddedResult.TargetObject.d.requestStatus.code -eq 601) { + # then check if we use the -Force. + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + $outputAddedResult = $null } - # and add it to our list of new metrics - $newEffects+= $from - $script:OBSFX[$from.EffectName] = $from - $from - } - } - } - - - -} - - -#.ExternalHelp obs-powershell-Help.xml -function Remove-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [Alias('Name')] - [string] - $EffectName - ) + } - begin { - if (-not $script:OBSFX) { - $script:OBSFX = [Ordered]@{} + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } } - } - - process { - if ($script:OBSFX[$name]) { - $script:OBSFX.Stop() - $script:OBSFX.Remove($name) + # Otherwise, if we had a result + if ($outputAddedResult -and + $outputAddedResult -isnot [Management.Automation.ErrorRecord]) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] } + } } #.ExternalHelp obs-powershell-Help.xml -function Start-OBSEffect -{ +function Set-OBSWindowSource { - [CmdletBinding(PositionalBinding=$false)] + + [Alias('Add-OBSWindowSource','Set-OBSWindowCaptureSource','Add-OBSWindowCaptureSource','Get-OBSWindowSource','Get-OBSWindowCaptureSource')] param( - # The name of the effect. - [ArgumentCompleter({ - param ( $commandName, - $parameterName, - $wordToComplete, - $commandAst, - $fakeBoundParameters ) - $effectNames = @(Get-OBSEffect| - Select-Object -Unique -ExpandProperty EffectName) - if ($wordToComplete) { - $toComplete = $wordToComplete -replace "^'" -replace "'$" - return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'") - } else { - return @($effectNames -replace '^', "'" -replace '$',"'") - } - })] - [Parameter(Mandatory)] - [string[]] - $EffectName, - - # The duration of the effect. - # If provided, all effects should use this duration. - # If not provided, each effect should use it's own duration. - [Timespan] - $Duration, - - # The parameters passed to the effect. - [Parameter(ValueFromPipelineByPropertyName)] - [Alias('EffectParameters')] - [Collections.IDictionary] - $EffectParameter = @{}, - - # The arguments passed to the effect. - [Parameter(ValueFromRemainingArguments)] - [Alias('EffectArguments')] - [PSObject[]] - $EffectArgument = @(), - - # If provided, will step thru running + # The monitor number. + # This the number of the monitor you would like to capture. [Parameter(ValueFromPipelineByPropertyName)] - [Alias('ticks')] - [int] - $Step, + [Alias('ItemValue','ItemName','WindowName','MainWindowTitle')] + [string] + $WindowTitle, - # The SceneItemID. If this is provided, the effect will be given a target. + # The number of the capture method. By default, automatic (0). [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("method")] [int] - $SceneItemID, + $CaptureMethod, - # The SceneName. If this is provided with a -SceneItemID or -SourceName, the effect will be given a target. + # The capture priority. [Parameter(ValueFromPipelineByPropertyName)] + [ValidateSet('ExactMatch','SameType','SameExecutable')] [string] - $SceneName, + $CapturePriority, - # The Filter Name. If this is provided with a -SourceName, the effect will be given a target. + # If set, will capture the cursor. + # This will be set by default. + # If explicitly set to false, the cursor will not be captured. [Parameter(ValueFromPipelineByPropertyName)] - [string] - $FilterName, + [ComponentModel.DefaultBindingProperty("cursor")] + [switch] + $CaptureCursor, - # The Source Name. If this is provided with a -FitlerName -or -SceneName, the effect will be given a target. + # If set, will capture the client area. + # This will be set by default. [Parameter(ValueFromPipelineByPropertyName)] - [string] - $SourceName, + [ComponentModel.DefaultBindingProperty("client_area")] + [switch] + $ClientArea, - # If set, will loop the effect. + # If set, will force SDR. + [Parameter(ValueFromPipelineByPropertyName)] + [ComponentModel.DefaultBindingProperty("force_sdr")] [switch] - $Loop, + $ForceSDR, - # If provided, will loop the effect a number of times. - [int] - $LoopCount, + # The name of the scene. + # If no scene name is provided, the current program scene will be used. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('SceneName')] + [string] + $Scene, - # If set, will bounce the effect (flip it / reverse it) - [switch] - $Bounce, + # The name of the input. + # If no name is provided, "Display $($Monitor + 1)" will be the input source name. + [Parameter(ValueFromPipelineByPropertyName)] + [Alias('InputName')] + [string] + $Name, - # If set, will reverse an effect. + # If set, will check if the source exists in the scene before creating it and removing any existing sources found. + # If not set, you will get an error if a source with the same name exists. + [Parameter(ValueFromPipelineByPropertyName)] [switch] - $Reverse + $Force ) + dynamicParam { + $baseCommand = + if (-not $script:AddOBSInput) { + $script:AddOBSInput = + $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function') + $script:AddOBSInput + } else { + $script:AddOBSInput + } + $IncludeParameter = @() + $ExcludeParameter = 'inputKind','sceneName','inputName' - process { - foreach ($NameOfEffect in $EffectName) { - $obsEffect = Get-OBSEffect -EffectName $NameOfEffect - if (-not $obsEffect) { - Write-Warning "No Effect named '$NameOfEffect'" - continue + $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) { + if ($ExcludeParameter) { + foreach ($exclude in $ExcludeParameter) { + if ($paramName -like $exclude) { continue nextInputParameter} } + } + if ($IncludeParameter) { + $shouldInclude = + foreach ($include in $IncludeParameter) { + if ($paramName -like $include) { $true;break} + } + if (-not $shouldInclude) { continue nextInputParameter } + } + + $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new( + $baseCommand.Parameters[$paramName].Name, + $baseCommand.Parameters[$paramName].ParameterType, + $baseCommand.Parameters[$paramName].Attributes + )) + } + $DynamicParameters - if ($LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount $LoopCount -Force - } + } + begin { + $InputKind = "window_capture" - if ($loop -or $Bounce) { - $obsEffect | Add-Member -MemberType NoteProperty Mode "$(if ($Bounce) {"Bounce"})$(if ($loop) {"Loop"})" -Force - if (-not $LoopCount) { - $obsEffect | Add-Member -MemberType NoteProperty LoopCount -1 -Force - } + } + process { + # Copy the bound parameters + $myParameters = [Ordered]@{} + $PSBoundParameters + # and determine the name of the invocation + $MyInvocationName = "$($MyInvocation.InvocationName)" + # Split it into verb and noun + $myVerb, $myNoun = $MyInvocationName -split '-' + # and get a copy of ourself that we can call with anonymous recursion. + $myScriptBlock = $MyInvocation.MyCommand.ScriptBlock + # Determine if the verb was get, + $IsGet = $myVerb -eq "Get" + # if no verb was used, + $NoVerb = $MyInvocationName -match '^[^\.\&][^-]+$' + # and if there were any other parameters then name + $NonNameParameters = @($PSBoundParameters.Keys) -ne 'Name' + + # If it is a get or there was no verb + if ($IsGet -or $NoVerb) { + $inputsOfKind = # Get all inputs of this kind + Get-OBSInput -InputKind $InputKind | + Where-Object { + if ($Name) { # If -Name was provided, + $_.InputName -like $Name # filter by name (as a wildcard). + } else { + $_ # otherwise, return every input. + } + } + + # If there were parameters other than name, + # and we were not explicitly called Get-* + if ($NonNameParameters -and -not $IsGet) { + # remove the name parameter + if ($myParameters.Name) { $myParameters.Remove('Name') } + # and pipe results back to ourself. + $inputsOfKind | & $myScriptBlock @myParameters } else { - $obsEffect | Add-Member -MemberType NoteProperty Mode "Once" -Force + # Otherwise, we're just getting the list of inputs + $inputsOfKind } + # (either way, if we were called Get- or with no verb, we're done now). + return + } + + if (-not $myParameters["Scene"]) { + $myParameters["Scene"] = Get-OBSCurrentProgramScene | + Select-Object -ExpandProperty currentProgramSceneName + } - if ($Reverse) { - $obsEffect.Reversed = $true + + if (-not $myParameters["WindowTitle"]) { + while ($_ -is [Diagnostics.Process] -and -not $_.MainWindowTitle) { + $_ = $_.Parent } - - if ($obsEffect -isnot [Management.Automation.CommandInfo]) { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - $obsEffect.Start() - - } else { - if ($step -and $obsEffect.Messages) { - $obsEffect.Step($step) - continue - } - - if (-not $this) { - if ($_.pstypenames -like '*.GetSourceFilter*') { - $this = $_ - } elseif ($FilterName -and $SourceName) { - $this = Get-OBSSourceFilter -SourceName $SourceName -FilterName $FilterName - } - - if ($_.pstypenames -like '*.GetSceneItem*') { - $this = $_ - } elseif ($SceneName -and ($SceneItemID -or $SourceName)) { - $this = - foreach ($sceneItem in Get-OBSSceneItem -SceneName $SceneName) { - if ($SceneItemID -and $sceneItem.SceneItemID -eq $SceneItemID) { - $sceneItem;break - } - elseif ($SceneName -and $sceneItem.SceneName -eq $SceneName) { - $sceneItem;break - } - } - } - } + if ($_.MainWindowTitle) { + $WindowTitle = $myParameters["WindowTitle"] = "$($_.MainWindowTitle)" + } + } - if ($Duration -and $obsEffect.Parameters.Duration) { - $EffectParameter.Duration = $Duration + # Window capture is a bit of a tricky one. + # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list. + # and for that, an input needs to exist. + if (-not $myParameters["Name"]) { + if ($myParameters["WindowTitle"]) { + $Name = $myParameters["Name"] = "WindowCapture-" + $myParameters["WindowTitle"] + } + else { + $Name = "WindowCapture" + } + } + + + + + $myParameterData = [Ordered]@{} + foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) { + + $bindToPropertyName = $null + + foreach ($attribute in $parameter.Attributes) { + if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) { + $bindToPropertyName = $attribute.Name + break } + } - $obsEffectOutput = . $obsEffect @EffectParameter @EffectArgument - if ($obsEffectOutput) { - $obsEffect | Add-Member NoteProperty Messages $obsEffectOutput -Force - if ($step) { - $obsEffect.Step($step) - } else { - $obsEffect.Start() - } + if (-not $bindToPropertyName) { continue } + if ($myParameters.Contains($parameter.Name)) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] + if ($myParameters[$parameter.Name] -is [switch]) { + $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name] -as [bool] } } - $obsEffect } - - - } -} -#.ExternalHelp obs-powershell-Help.xml -function Stop-OBSEffect -{ - - param( - # The name of the effect. - [Parameter(Mandatory,ValueFromPipelineByPropertyName)] - [string] - $EffectName) - process { - $obsEffect = Get-OBSEffect -EffectName $EffectName + if ($null -ne $CaptureMethod) { + $myParameterData["method"] = $CaptureMethod + } + if ($CapturePriority -eq 'ExactMatch') { + $myParameterData["priority"] = 1 + } + elseif ($CapturePriority -eq 'SameType') { + $myParameterData["priority"] = 0 + } + elseif ($CapturePriority -eq 'SameExecutable') { + $myParameterData["priority"] = 2 + } - if (-not $obsEffect) { return } + $addSplat = @{ + sceneName = $myParameters["Scene"] + inputName = $myParameters["Name"] + inputKind = "window_capture" + inputSettings = $myParameterData + NoResponse = $myParameters["NoResponse"] + } - $obsEffect | Add-Member -MemberType NoteProperty Mode 'Stopped' -Force + # If -SceneItemEnabled was passed, + if ($myParameters.Contains('SceneItemEnabled')) { + # propagate it to Add-OBSInput. + $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool] + } + + # Add the input. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + $possibleWindows = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName window + foreach ($windowInfo in $possibleWindows) { + if (-not $WindowTitle) { continue } + if ( + ($windowInfo.itemName -eq $WindowTitle) -or + ($windowInfo.ItemValue -eq $WindowTitle) -or + ($windowInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $WindowTitle) -or + ($windowInfo.ItemValue -like "*$WindowTitle*") -or + ($windowInfo.ItemName -like "*$WindowTitle*") + ) { + $myParameterData["window"] = $windowInfo.itemValue + break + } + } + + # If -PassThru was passed + if ($MyParameters["PassThru"]) { + # pass it down to each command + # Otherwise, remove SceneItemEnabled, InputKind, and SceneName + $addSplat.PassThru = $true + $addSplat.Remove('SceneItemEnabled') + $addSplat.Remove('inputKind') + $addSplat.Remove('sceneName') + # and passthru Set-OBSInputSettings. + Set-OBSInputSettings @addSplat + + return + } + + if ($Force) { # If we do, remove the input + Remove-OBSInput -InputName $addSplat.inputName + # and re-add our result. + $outputAddedResult = Add-OBSInput @addSplat *>&1 + # If the output was still an error + if ($outputAddedResult -is [Management.Automation.ErrorRecord]) { + # use $psCmdlet.WriteError so that it shows the error correctly. + $psCmdlet.WriteError($outputAddedResult) + } + # Otherwise, if we had a result + elseif ($outputAddedResult) { + # get the input from the scene. + Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $name + } + } else { + # Otherwise, get the input from the scene, + $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] | + Where-Object SourceName -eq $myParameters["Name"] + # update the input settings + $sceneItem.Input.Settings = $addSplat.inputSettings + $sceneItem # and return the scene item. + } + } -} \ No newline at end of file +}